Fork me on GitHub
#clara
<
2021-05-12
>
mathias_dw19:05:27

Hi, I ran into some unexpected behavior today. I managed to replicate it in a small example (not sure if it's minimal):

(defrecord A [n])
(defrecord B [m])
(defrecord C [m n])
(defrule loop-rule
  [B (= ?m m)]
  [C (= ?m m) (= ?n n)]
  [:not [A (= ?n n)]]
  =>
  (println (str "A: " ?n))
  (insert! (->A ?n)))

(-> (mk-session :cache false)
    (insert (->B 1))
    (insert (->C 1 2))
    (insert (->C 1 3))
    (fire-rules))
This results in an infinite loop. So I guess the rule is triggering on each insertion of a new A, always finding the other C to make things fire? I expected the A to be inserted just twice. Note that in my real life use case, the A type is also coming from another source, so that's why I'm checking it. Am I simply not understanding the :not?

ethanc19:05:03

The rule itself is contradicting itself

đź‘Ť 3
mathias_dw19:05:40

Ah, yes, now I get it

ethanc19:05:46

or perhaps it could be thought of as a self fulfilling prophecy

mathias_dw19:05:54

thanks for the fast reply!

mathias_dw19:05:34

and thanks for clara: I'm building a pretty product and it's been a joy to work with 🙂

❤️ 5
mathias_dw19:05:23

*pretty complex product

ethanc20:05:42

The minimal example of the looping behavior would probably be something like:

(defrecord A [n])
(clara/defrule not-a
  [:not [A]]
  =>
  (clara/insert! (->A 12)))
Typically to prevent this loop i would use something like a “reason” field on the fact if possible to denote default from other facts. Something like:
(defrecord A [n reason])

(r/defrule not-a
  [:not [A (not= ::default reason)]]
  =>
  (r/insert! (->A 12 ::default)))

đź‘Ť 2