Last active
July 1, 2020 18:07
-
-
Save mrrodriguez/6a6f8373b25d69826b3efe154c928fac to your computer and use it in GitHub Desktop.
Clara tiered fact update rules
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(require '[clara.rules :as r]) | |
;;;; Define 3 rules, where the "priority" order is r1, r2, r3, where the highest priority is first | |
;;;; and the rest is in descending order of priority. | |
;;;; :type :rule/result "syntetic" fact is used to hold the final changes that can be queried out | |
;;;; from a session after `r/fire-rules` via `r/query` on the `find-results` query. | |
;;;; A namespace qualified keyword is used to avoid collision with externally given :type of | |
;;;; "real" facts. | |
;;;; To ensure the priority order of rules is upheld each lower priority rule checks that no higher | |
;;;; priority rule has already fired. This common pattern can be broken out by a higher-level rule | |
;;;; generation scheme if it becomes difficult to maintain. | |
;;;; The original post problem statement does not describe whether the facts have any sort of identifier | |
;;;; on them. Since this is an unknown the :id stored on the :rule/result is the whole fact. The :id | |
;;;; is used to ensure the same fact is not updated twice by rules of different priorities. | |
(r/defrule r1 | |
[?f <- :type-a [{:keys [value]}] (= value :x)] ;; <- looks for :value :x's | |
=> | |
(r/insert! {:type :rule/result | |
:from :r1 | |
:id ?f | |
:fact (assoc ?f :key-from-r1 1)})) | |
(r/defrule r2 | |
[?f <- :type-a [{:keys [value]}] (= value :y)] ;; <- looks for :value :y's | |
[:not [:rule/result [{:keys [from id]}] | |
(= from :r1) | |
(= ?f id)]] | |
=> | |
(r/insert! {:type :rule/result | |
:from :r2 | |
:id ?f | |
:fact (assoc ?f :key-from-r2 2)})) | |
(r/defrule r3 | |
[?f <- :type-a] ;; <- catch all facts not matched by r1 or r2 | |
[:not [:rule/result [{:keys [from id]}] | |
(contains? #{:r1 :r2} from) | |
(= ?f id)]] | |
=> | |
(r/insert! {:type :rule/result | |
:from :r3 | |
:id ?f | |
:fact (assoc ?f :key-from-r3 3)})) | |
(r/defquery find-results [] | |
[:rule/result [{:keys [fact]}] (= ?fact fact)]) | |
;;; If you are unfamiliar with the :fact-type-fn for `r/mk-session` see | |
;;; http://www.clara-rules.org/docs/fact_type_customization/ | |
(def fact-type-fn :type) | |
(def s | |
(r/mk-session [r1 r2 r3 find-results] | |
:fact-type-fn fact-type-fn)) | |
;;; :value :x matches `r1` | |
;;; :value :y matches `r2` | |
;;; :value :z (any non :x or :y value) matches `r3` | |
(let [qres (-> s | |
(r/insert {:type :type-a | |
:value :x | |
:tstamp 1} | |
{:type :type-a | |
:value :y | |
:tstamp 2} | |
{:type :type-a | |
:value :y | |
:tstamp 3} | |
{:type :type-a | |
:value :z | |
:tstamp 4}) | |
r/fire-rules | |
(r/query find-results))] | |
(mapv :?fact qres)) | |
(comment | |
;; Returns | |
[{:type :type-a, :value :x, :tstamp 1, :key-from-r1 1} | |
{:type :type-a, :value :y, :tstamp 3, :key-from-r2 2} | |
{:type :type-a, :value :y, :tstamp 2, :key-from-r2 2} | |
{:type :type-a, :value :z, :tstamp 4, :key-from-r3 3}]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment