This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-01-19
Channels
- # beginners (34)
- # boot (111)
- # cider (37)
- # clara (57)
- # cljsjs (1)
- # cljsrn (22)
- # clojure (156)
- # clojure-austin (2)
- # clojure-mke (7)
- # clojure-russia (9)
- # clojure-spec (221)
- # clojure-uk (47)
- # clojurescript (42)
- # code-reviews (4)
- # community-development (9)
- # core-async (3)
- # cursive (50)
- # datomic (81)
- # emacs (12)
- # events (5)
- # hoplon (1)
- # jobs (2)
- # lein-figwheel (4)
- # leiningen (1)
- # luminus (3)
- # mount (2)
- # off-topic (1)
- # om (94)
- # om-next (3)
- # onyx (33)
- # re-frame (23)
- # reagent (41)
- # remote-jobs (9)
- # rum (30)
- # slack-help (2)
- # specter (1)
- # untangled (20)
- # yada (17)
do I understand correctly that a rule like the one in https://github.com/cerner/clara-examples/blob/master/src/main/clojure/clara/examples/sensors.clj#L48:
(defrule get-current-temperature
"Get the current temperature at a location by simply looking at the newest reading."
[?current-temp <- newest-temp :from [TemperatureReading (= ?location location)]]
=>
(insert! (->CurrentTemperature (:value ?current-temp) ?location)))
will accumulate CurrentTemperature
facts, never retracting them?seems the above is retracted on every new TemperatureReading
. Is my intuition right in this case - the retraction happens because when the new TemperatureReading
(t+1) arrives, the condition under which the previous CurrentTemperature
became true (t+0) is now false?
When the conditions that caused a logical fact insertion to occur Clara will adjust the session to reflect the new status of those conditions
If you used insert-unconditional! you’d end up with multiple CurrentTemperature facts potentially depending on your rules firing pattern
But as long as you stay away from uncondition insertions and manual fact retractions inside rules you can just think of the rules as pure logical relationships that “if as session has these things in it it will also have these other things in it"
After you call fire-rules at least, i.e. (-> your-session (insert your-facts-here) ;;no-contract-about-status-now fire-rules ;; now-there-is-a-contract-about-the-status)
So if you have TemperatureReading facts with different locations you can have a CurrentTemperature fact for each distinct location
what if I have facts that change with time and do an insert CurrentTimestamp -> fire-rules -> retract CurrentTimestamp
periodically? Are there any obvious issues I'll run into?
@dm3 As long as you use truth maintenance Clara will resolve all transitive impacts on rules
i.e. if you insert Fact1 which causes RuleA to insert fact2, and fact2 causes ruleB to insert fact3
a retraction of fact1 will cause fact3 to be removed from the session if appropriate based on the rules
If you do something like (insert JavaBeanFact) fire-rules (.setSomeFieldUsedInRules JavaBeanFact newValue)
if you want to change fields you’ll need to retract the fact and then insert a new one with the modifications you want
Actually I’d consider mutation of facts after insertion to result in undefined behavior in general, apart from whether the field is used
Mutating fields after insertion used in rules would definitely cause problems with the current implementation, would need to think about mutations not impacting rule bindings but either way the behavior isn’t guaranteed and I wouldn’t advise doing it
I'm thinking of batching the input stream of events and reducing the batch with (fn [sess batch] (-> (insert sess timestamp) (insert-all batch) (fire-rules) (retract timestamp)))
yeah obviously the existence of problems depends on the specific use-case but I don’t see problems in general with inserting and then retracting a timestamp fact that would have downstream impact through various rules
I'm now trying to do the following:
(ns rulebot.test
(:require [clara.rules :as clara]
[clara.rules.accumulators :as acc]
[clara.tools.tracing :as tt]))
(defrecord Value [value instant])
(defrecord Step [step instant])
(defrecord CurrentValue [value])
(defrecord CurrentStep [step])
(defrecord CurrentTimestamp [instant])
(clara/defrule current-value
[?e <- (acc/max :instant :returns-fact true) :from [Value]]
=>
(clara/insert! (->CurrentValue (:value ?e))))
(clara/defrule current-step
[?e <- (acc/max :instant :returns-fact true) :from [Step]]
=>
(clara/insert! (->CurrentStep (:step ?e))))
(clara/defrule step
[CurrentValue (= ?value value)]
[CurrentStep (= ?prev-step step)
(= ?curr-step (int (/ ?value 10)))]
[CurrentTimestamp (= ?instant instant)]
[:test (> ?curr-step ?prev-step)]
=>
(clara/insert! (->Step ?curr-step ?instant)))
(defn go []
(let [session (-> (clara/mk-session 'rulebot.test)
(tt/with-tracing))]
(-> session
(clara/insert (->CurrentTimestamp 0))
(clara/insert (->Value 1 0))
(clara/insert (->Step 0 0))
(clara/fire-rules)
(clara/retract (->CurrentTimestamp 0))
(clara/insert (->CurrentTimestamp 1))
(clara/insert (->Value 11 1))
(clara/fire-rules)
(tt/get-trace))))
which fails on the step
rule because ?prev-step
gets bound to an {:ns-name .., :lhs ..., :rhs ...}
map for some reason...{:ns-name .., :lhs ..., :rhs …} is what the internal representation of a rule looks like; its the map structure that defrule stores in a var
I don’t have time to look into it in detail at the moment but I’d guess that the “step” field is being resolved to the “step” var instead
I’d say this edge case merits a GitHub issue to determine what to do with it but for the moment I think you can resolve this by naming your rule something that isn’t the name of one of your fact fields
I see you’re inserting Step in one of your rules that looks for facts that are inserted due to Step
At first glance I’d say you’re likely doing something where you’re inserting a Step that then causes itself to be retracted and entering an infinite loop that way
the step
rule does insert a new Step
, which should then retract the old CurrentStep
and insert a new one, unless I'm misunderstanding something
however, after the step
rule runs, the current-step
rule is still executed with the previous Step
fact
it's probably the truth maintenance screwing with me - Step (t0)
is inserted outside the rules, Step (t1)
is inserted by the next-step
rule with CurrentStep (t0)
. After Step (t1)
triggers current-step
to retract the CurrentStep (t0)
, the Step (t1)
also gets retracted which again triggers next-step
with CurrentStep (t0)
what should I do in order to accumulate the Step
facts (more like events in this case) instead of having the above happening?
to be honest I’m not sure what sort of logic you’re trying to represent with the recursion there so an alternative isn’t obvious to me
I'm trying to capture increases in Value
as Step
s. There's a stream of Value
s coming in, which I batch and fire
with a new CurrentTimestamp
every N ms. Some of the batches might trigger a new Step
, which I've imagined would appear as a new fact in the session. I guess the culprit of my troubles is the recursive relation between the CurrentStep
and Step
.
tried reformulating the next-step
rule as follows (becuase the CurrentStep is just the MAX of all steps):
(clara/defrule next-step
[CurrentValue (= ?value value)]
[?s <- (acc/max :step :returns-fact true) :from [Step (= ?prev-step step)]]
[CurrentTimestamp (= ?instant instant)]
[:test (> (int (/ ?value 10)) ?prev-step)]
=>
(clara/insert! (->Step (int (/ ?value 10)) ?instant)))