That works. Thank you!
Just for my own edification: why :exists at all? Does it work in the non-negated case?
Yes, the parsing of the graph in a non-negation flow handles it correctly. The :exists sugar has always baffled me, in concept it should make a rule more human readable, ie. if X exists then ....
However, personally i rarely/never use it
Well… i guess its a known issue: https://github.com/cerner/clara-rules/issues/329 However, the “schema” error seems to have been lost some where in the updates to the compiler. The issue as it stands:
#clara.rules.engine.NegationNode{:id 1,
:condition [:exists {:type Stuff, :constraints []}],
:children (#clara.rules.engine.ProductionNode{:id 2,
:production {:ns-name
:lhs [[:not
[:exists
{:type Stuff,
:constraints []}]]],
:rhs (do
(println "Add stuff")
(r/insert!
(->MakeStuff
(->Stuff "foo")))),
:name "add-stuff"},
:rhs #object[eval9894$fn__9895
0x386ba022
"eval9894$fn__9895@386ba022"]}),
:binding-keys #{}}
This negation node makes no sense… It has no parent, meaning that post session construction it has no interaction with the session. Something equivalent to a rule with no LHS…. just “do” the RHS.Oh. So it's going to run once no matter what? I guess I could test that.
Verified.
The compiler is extracting this portion of the graph all wrong.
One of the telling bits about this one was, when attaching a listener to the session... the log message from the rule is printed prior to the first interaction with the listener.
This is indicative of:
https://github.com/cerner/clara-rules/blob/5098246a80b521ddc82cf483b4caceba635dc426/src/main/clojure/clara/rules/engine.cljc#L2063-L2069
the "left-activate" here is provided with a "default-listener"(No-Op) as its pushing basically a "none token". This sort of bootstrapping is catching nodes without a proper parent, the fact that this is triggering the negation means that the :exist and its condition are being ignored all together.
Is the workaround to do something like a count accumulator and test against zero?
The quick solution would most likely be to replace that sort of rule with something closer to:
(r/defrule add-stuff
[:not [Stuff]]
=>
(println "Add stuff")
(r/insert! (->MakeStuff (->Stuff "foo"))))Which should have the desired effect without the accumulator
Ah. That makes more sense anyway. I'll give that a try.