I'm just trying out clara rules for the first time, and I am trying to express the following idea: I have a bunch of facts. Based on some condition from the facts I want to insert a fact "DataRequest", but only if one doesn't already exist The way I'm testing this right now is like so:
(defrecord Field [name value])
(defrecord DataRequest [data-partner])
(defrule enrich-fields
[Field (= ?name name)]
[:not [DataRequest (= data-partner ?name)]]
=>
(r/insert! (->DataRequest ?name)))
The problem is that when I do this, it goes into an infinite loop, which I had thought this [:not
section would prevent. Am I missing something obvious?I see now the problem here is that the rule invalidates itself, causing truth maintenance to trigger, and re-evaluating the rule immediately.
I have an additional question: Is there a way to migrate sessions from an old rulebase to a new one? I understand this causes serious problems if there's substantial change in the rules, but if for example there is only accretion of new rules and old rules are not changed, is there a way to do this while keeping truth support? As I was thinking about this what I'd wanted to do was to query all the facts in the working memory and insert them into a new session, but this loses truth support, seemingly because clara uses identity to track records rather than clojure equality.
Clara doesn’t support adding rules to an existing session at present. However Clara is meant to use equality to track facts. There are performance optimisations around when things are identical in places, but if this is actually changing behaviour then that would be a bug.
The reason that I say things are tracked by identity and not equality is that if I insert two facts into a working memory, one which would be equal to a fact generated by a rule that operates on the other, then I get two copies of that fact.
Rather than the rule realizing that the action has already occurred because the derived fact is already in the working memory.
This happens because Clara allows multiple equal facts to exist in the session, and tracks the number of them inserted. So after something like (-> session (insert a) (insert a) fire-rules (retract a) you’d still have on “a” in the session.
The same applies for facts created as a result of rules firing.
If this isn’t desirable in your use-case the distinct accumulator can be used.