Fork me on GitHub
#clara
<
2021-12-07
>
pdmct04:12:13

Hi, I’m very new to clara and trying to use it to build an hierarchical data structure. I am having some trouble grasping the basics obviously. I have this following simple rule that I want to create one widget:

(defrecord Widget [material type num-gadgets])

(defn pick-type [type]
  "intermediate")

(defn get-num-gadgets []
  1)

(defrule add-widget
  [?c <- (accum/count) :from (Widget (= material "steel"))]
  [:test (= 0 ?c)]
  =>
  (println "adding widget")
  (rules/insert! (->Widget "steel" (pick-type "foo") (get-num-gadgets))))
I expect that the test should resolve to false once it has created one widget, however this seems to loop forever. What am I doing wrong here? I am also not sure if this ‘right’ way to create facts in the session?

pdmct08:12:15

Ok, after scrolling back … seems this is a common trap that newbies fall into with truth maintenance. Walking through an example of something like this would make a great example in the getting started documentation!

pdmct09:12:01

I am trying to build an object generator using the rules, here is a example:

(defrecord Widget [name material length parts])

(defrule add-widget
  [:not [Widget]]
  =>
  (println "adding widget")
  (rules/insert-unconditional! (->Widget "w1" "unknown" nil [])))

(defn pick-material []
  (first (shuffle ["steel" "silver" "gold"])))
(defn pick-length [mat]
  (condp = mat
    "steel" (+ 10 (rand-int 20))
    "silver" (+ 5 (rand-int 10))
    "gold" (rand-int 10)))

(defrule set-widget-attrs
  [?widget <- Widget (= ?name name) (= ?material material) (= ?length length) (= ?parts parts)]
  [:test (and (nil? ?length) (= ?material "unknown"))]
  =>
  (println "setting material")
  (let [new-mat (pick-material)
        len (pick-length new-mat)]
    (rules/insert-unconditional! (->Widget ?name new-mat len ?parts)))
  (rules/retract! ?widget))
this runs but produces 2 widgets (fires twice) and I am not sure why. I am trying to effectively update the widget attributes by creating a new one and retracting the old one. This is the output:
(def session (-> (rules/mk-session 'user)               
               (rules/fire-rules)))
adding widget
setting material
adding widget
setting material

(rules/query session widget?)
=>List(2)(
     0: Map{:?widget: #user.Widget Map{:name: "w1", :material: "gold", :length: 4, :parts: Vector(0)}},
     1: Map{:?widget: #user.Widget Map{:name: "w1", :material: "gold", :length: 0, :parts: Vector(0)}}
)

pdmct09:12:42

If I remove the retract it only runs once, but leaves the fact with unknown material around … I guess my question is what is the usual way to do this sort of thing? do I just leave these intermediate facts around , or can I clean them up some how?

pdmct09:12:36

without the retract!:

List(2)(
0: Map{:?widget: #user.Widget Map{:name: "w1", :material: "gold", :length: 4, :parts: Vector(0)}},
1: Map{:?widget: #user.Widget Map{:name: "w1", :material: "unknown", :length: nil, :parts: Vector(0)}})