Fork me on GitHub
#clara
<
2017-10-13
>
mmer09:10:30

Is there a way to change facts that have been inserted - what I want to do is to change the Records to include more information as it becomes available.

mmer12:10:42

Another question - is it possible to reference the name of a rule in a rule?

zylox15:10:51

as for the first question, you would want to retract the original and insert the newer one. This will require some logic outside of the rules network. Clara assumes immutability in its facts im pretty sure.

zylox15:10:43

second one: What are you wanting to do with a rule reference? You should be relying on the consequences of the right hand side of said rule on the state of the facts in the session to infer things. Maybe if you had a more specific case of what you are trying to do i can provide more specific advice there.

mikerod15:10:01

> as for the first question, you would want to retract the original and insert the newer one. This will require some logic outside of the rules network. Clara assumes immutability in its facts im pretty sure. Yeah, I think that is common. If you aren’t concerned with a constantly growing working memory, i.e. append-only, you can get by with doing it all in rules

mikerod15:10:21

It seems to me that Clara should offer something to better support the “update” pattern

mikerod15:10:25

Something like:

(r/defrule update-fact
  [?current <- FactType (= ?id id)]
  [?update <- (acc/max :timestamp) :from [UpdateFact (= ?id id)]]
  =>
  (r/retract! ?current) ;; or if you wanted to remove updates altogether - with no accumulator on LHS `(apply r/retract! [?current ?update])`
  (r/insert! (do-update ?current ?update)))

To me seems like a pretty natural thing to do, but it violates truth maintenance since the supporting facts ?current and ?update are retracted that logically supported the r/insert!

mikerod15:10:02

However, r/retract! is a bit at odds with Truth maintenance since it is “order-dependent”

mikerod15:10:35

if you r/retract! something, but later the rule is found to be invalidated by some other retractions, you’d expect the r/retract! operation to be rolled back

mikerod16:10:39

if you do append-only style, I think something like this works though:

(defrecord UpdateFact [id timestamp stuff])
(defrecord FactType [id timestamp x y])
(defrecord FactTypeSnapshot [id timestamp x y])

(r/defrule current-fact
  [?current <- (acc/max :timestamp) :from [FactTypeSnapshot (= ?id id)]]
  =>
  (r/insert! (map->FactType ?current)))

(r/defrule update-fact-type

  [?update <- (acc/all) :from [UpdateFact (= ?id id) (= ?ts timestamp)]]
  [?current <- FactType (= ?id id) (< timestamp ?ts)]
  =>

  ;; Inserts a `FactTypeSnapshot w/ updates applied and `:timestamp` = `?ts`
  (r/insert! (do-update ?current ?update ?ts)))

(r/defrule using-fact-type
  [FactType (= ?id id) (= ?x x) (= ?y y)]
  =>
  (prn ?x))


mikerod16:10:14

The key is to have way to represent snapshots of a fact over time - the FactTypeSnapshot, a way to select the one you want to use in rules - the FactType, a way to apply updates - via UpdateFact to those facts in a way that doesn’t cause a logical loop. Didn’t test this specifically, but I believe the idea plays out alright (for append-only working memory). Externally may tend to be smoother, as @zylox said, so often I’d go with that. I wonder if Clara could provide some sort of helpers for that though

;;; Add a query to your ruleset to fetch facts you want to update 
(r/defquery find-fact [find-id]
  [?fact <- FactType (= id find-id)])

;;; Example external update fn
(defn do-update [session update]

  ;; Assumes :id uniquely identifies facts here
  (let [old (first (mapv :?fact (r/query sesssion find-fact :id (:id update))))]
    (-> session
        (r/retract old)
        (r/insert (update-fact old update))
        r/fire-rules)))