clara

http://www.clara-rules.org/
2021-05-12T19:53:27.078700Z

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?

ethanc 2021-05-12T19:55:23.079100Z

I think http://www.clara-rules.org/docs/truthmaint/ might be a good start.

ethanc 2021-05-12T19:56:03.079600Z

The rule itself is contradicting itself

πŸ‘ 1
2021-05-12T19:56:40.080500Z

Ah, yes, now I get it

ethanc 2021-05-12T19:56:46.080800Z

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

2021-05-12T19:56:54.081100Z

thanks for the fast reply!

2021-05-12T19:57:34.082Z

and thanks for clara: I'm building a pretty product and it's been a joy to work with πŸ™‚

❀️ 2
2021-05-12T19:58:23.082400Z

*pretty complex product

ethanc 2021-05-12T20:17:42.084600Z

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)))

πŸ‘ 1