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.
?- 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:
Match (find entities in rule)
Resolve (check rules)
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:
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), and RETE rule engine design.
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.
Keep in mind, you don’t have a duplicate facts, i.e. there’s no:
cat(black).
cat(black).
only
cat(black).
So you need to be explicit that these facts are, in fact, separate, so:
cat(A, Color), cat(B, Color) <=> A #= B | cat_pair(A,B,Color).
Give facts identifier of some sort.
In this case you have:
?- 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
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:
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.
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.