# Tricky CHR behaviour

**URL:** <https://swi-prolog.discourse.group/t/tricky-chr-behaviour/9642>\
**Category:** Help!\
**Created:** [June 5, 2026, 6:59am UTC](https://swi-prolog.discourse.group/t/tricky-chr-behaviour/9642 "2026-06-05T06:59:08Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![AZH](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/azh/32/6831_2.png) [@AZH](https://swi-prolog.discourse.group/u/AZH)\
**Post date:** [June 5, 2026, 6:59am UTC](https://swi-prolog.discourse.group/t/tricky-chr-behaviour/9642/1 "2026-06-05T06:59:08Z")

</div>

Hello, I have a CHR as follows:

```prolog
:- use_module(library(chr)).
:- chr_constraint try/1.

try(X), try(Y) <=> X == Y | writeln(succeed).

```

then I declared a constraint, but got an unexpected result:

```prolog
?- try(A).
succeed
try(A).

```

The unexpected `succeed` seems to be because Prolog matches `try(A)` to both `try(X)` and `try(Y)`. But does this make sense?

Thanks in advance! 🙂

---

<div class="post-metadata">

**Author:** ![Ian](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/ian/32/1026_2.png) [@Ian](https://swi-prolog.discourse.group/u/Ian)\
**Post date:** [June 5, 2026, 9:30am UTC](https://swi-prolog.discourse.group/t/tricky-chr-behaviour/9642/2 "2026-06-05T09:30:30Z")

</div>

See [A strange behavior of CHR package](https://swi-prolog.discourse.group/t/a-strange-behavior-of-chr-package/4964)

However, if you only have grounded constraints, then you should be okay. try(X) by itself doesn’t make sense (at least to me) as a constraint.

---

<div class="post-metadata">

**Author:** ![AZH](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/azh/32/6831_2.png) [@AZH](https://swi-prolog.discourse.group/u/AZH)\
**Post date:** [June 5, 2026, 11:17am UTC](https://swi-prolog.discourse.group/t/tricky-chr-behaviour/9642/3 "2026-06-05T11:17:47Z")

</div>

Thanks Ian!  
From the post your shared, it is a pretty old issue.  
Hope this will be fixed, sooner rather than later…

---

<div class="post-metadata">

**Author:** ![abaljeu](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/abaljeu/32/136_2.png) [@abaljeu](https://swi-prolog.discourse.group/u/abaljeu)\
**Post date:** [June 13, 2026, 11:43pm UTC](https://swi-prolog.discourse.group/t/tricky-chr-behaviour/9642/4 "2026-06-13T23:43:26Z")

</div>

CHR is not in active development. It is open source and could be fixed, but i dont know any experts on the internals are around to do so. Seems like something i would have to tackle if i got back into chr, but its not presently my focus.

---

<div class="post-metadata">

**Author:** ![xlii](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/xlii/32/7931_2.png) [@xlii](https://swi-prolog.discourse.group/u/xlii)\
**Post date:** [June 15, 2026, 7:32am UTC](https://swi-prolog.discourse.group/t/tricky-chr-behaviour/9642/5 "2026-06-15T07:32:24Z")

</div>

I was scratching my head why this could be unexpected. I haven’t touched CLIPS for a while but I’m quite confident that it behaves the same. I’m 99% sure that this is the same behavior Soufflé uses too. It also aligns with my understading of RETE rule engines.

For me the dissection is as follows:

```prolog
?- try(A). % Add fact to the store

try(X), % Is there a fact try(X) => YES - try(_999).
try(Y) % Is there a fact try(Y) => YES - try(_999).
<=>
X == Y % Does X == Y? => YES, because _999 is _999.
| writeln(succeed).

```

To those unfamiliar with RETE - there are 3 phases of rule resolutions:

1. Match (find entities in rule)
2. Resolve (check rules)
3. Act (fire actual rules)

In Match phase there is no store modification (this happens in Act phase) nor conflict resolution (which is Resolve phase). Match phase is completely isolated from store and guards.

An example to consider, as well:

```prolog
number(4), number(X) <=> X < 5 | writeln(succeed).
?- number(4).

```

And finally: If match phase would be sequential it would be impossible to parallelize queries. For naive constructs it doesn’t matter but in production each part of match query can and probably should be ran in parallel, so there’s no “knowledge” between the threads/systems which entity was “yanked” from the store.

_For me_ this is completely intutive, though I spent considerate time with CHR systems (even designed a rule based parser in CLIPS: [Crazy Parser Idea](https://xlii.space/eng/parser-idea/)), and RETE rule engine design.

---

<div class="post-metadata">

**Author:** ![AZH](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/azh/32/6831_2.png) [@AZH](https://swi-prolog.discourse.group/u/AZH)\
**Post date:** [June 15, 2026, 9:22am UTC](https://swi-prolog.discourse.group/t/tricky-chr-behaviour/9642/6 "2026-06-15T09:22:05Z")

</div>

Thanks @xlii, that explains a lot!

Now that I understand the mechanism and can adjust the expectation of how it behaves. However, the actual problem remains unsolved. Essentially the CHR guard needed here is to compare any two separate try/1 facts, iff there are at least two separate try/1 facts. I guess this may be a pretty common scenario when we input the CHR facts one by one into the store. But with my limited skill, I don’t know how to implement that.

Any workaround? Much appreciated!

---

<div class="post-metadata">

**Author:** ![AZH](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/azh/32/6831_2.png) [@AZH](https://swi-prolog.discourse.group/u/AZH)\
**Post date:** [June 15, 2026, 9:41am UTC](https://swi-prolog.discourse.group/t/tricky-chr-behaviour/9642/7 "2026-06-15T09:41:36Z")

</div>

In addition to the last message, I’ve found something interesting.

We can get a different result, if we disable the printout of CHR store:

```prolog
:- use_module(library(chr)).
:- chr_constraint try/1.

:- set_prolog_flag(chr_toplevel_show_store, false).

try(X), try(Y) <=> X == Y | writeln(succeed).

```

Then it works as I want it to.

```prolog
?- try(A).
true.

```

Pretty amazing…

---

<div class="post-metadata">

**Author:** ![xlii](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/xlii/32/7931_2.png) [@xlii](https://swi-prolog.discourse.group/u/xlii)\
**Post date:** [June 15, 2026, 9:47am UTC](https://swi-prolog.discourse.group/t/tricky-chr-behaviour/9642/8 "2026-06-15T09:47:02Z")

</div>

Keep in mind, you don’t have a duplicate facts, i.e. there’s no:

```prolog
cat(black).
cat(black).

```

only

```prolog
cat(black).

```

So you need to be explicit that these facts are, in fact, separate, so:

```prolog
cat(A, Color), cat(B, Color) <=> A #= B | cat_pair(A,B,Color).

```

Give facts identifier of some sort.

In this case you have:

```prolog
?- cat(spooky, black).
% Nothing runs, because the only match is cat(spooky, black), cat(spooky,black)
% and spooky == spooky.
?- cat(cookie, black).
% 3 pairs found: (spooky,spooky), (cookie,cookie), (cookie, spooky)
% 
% Why not (spooky, cookie) and (cookie, spooky)? - RETE engine Matches are unordered

% cat(spooky,black), cat(spooky, black) NO MATCH
% cat(cookie,black), cat(cookie, black) NO MATCH
% cat(cookie,black), cat(spooky, black) MATCH -> ACT

```

---

<div class="post-metadata">

**Author:** ![xlii](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/xlii/32/7931_2.png) [@xlii](https://swi-prolog.discourse.group/u/xlii)\
**Post date:** [June 15, 2026, 9:51am UTC](https://swi-prolog.discourse.group/t/tricky-chr-behaviour/9642/9 "2026-06-15T09:51:23Z")

</div>

> [@AZH](#):
>
> We can get a different result, if we disable the printout of CHR store:

This might be actually confusing.  
I’m not sure what’s the phase of the printout (empty store wouldn’t make any printout, I suspect this is after-fact before-rule). Would have to check the docs.

Other explanation would be that non-concrete fact cannot be removed from store, because at that moment it could be that:

```prolog
store: try(_9999)
act: remove(try(_1111))
result: [try(_9999)] sub try(_1111) => [try(_9999)]

```

I propose checking this with manual printout of store - should tell more than a flag.

---

<div class="post-metadata">

**Author:** ![AZH](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/azh/32/6831_2.png) [@AZH](https://swi-prolog.discourse.group/u/AZH)\
**Post date:** [June 15, 2026, 9:55am UTC](https://swi-prolog.discourse.group/t/tricky-chr-behaviour/9642/10 "2026-06-15T09:55:41Z")

</div>

Thanks @xlii , assigning each fact a unique identifier is a solid solution!

---

<div class="post-metadata">

**Author:** ![abaljeu](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/abaljeu/32/136_2.png) [@abaljeu](https://swi-prolog.discourse.group/u/abaljeu)\
**Post date:** [June 26, 2026, 8:17pm UTC](https://swi-prolog.discourse.group/t/tricky-chr-behaviour/9642/11 "2026-06-26T20:17:26Z")

</div>

CHR will have a rule like a(X) \ a(X) \<=\> true to eliminate duplicates. If that fired when you only have a single a(4), it would be bad.

Efficiency note: it doesn’t matter for toy problems, but at scale, with optimizations on, the optimizations including hashing on integers. So using \_vars is convenient but if you’re tagging your terms for uniqueness a ground identifier is better.
