This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-11-13
Channels
- # beginners (71)
- # boot (61)
- # clara (49)
- # cljs-dev (9)
- # cljsjs (2)
- # cljsrn (5)
- # clojure (55)
- # clojure-android (1)
- # clojure-italy (4)
- # clojure-spec (39)
- # clojure-uk (56)
- # clojurescript (69)
- # cursive (5)
- # data-science (1)
- # defnpodcast (6)
- # devcards (1)
- # duct (12)
- # figwheel (3)
- # fulcro (18)
- # leiningen (35)
- # lumo (19)
- # midje (1)
- # off-topic (22)
- # om (3)
- # onyx (23)
- # portkey (3)
- # re-frame (20)
- # reagent (23)
- # ring-swagger (6)
- # shadow-cljs (119)
- # specter (7)
- # unrepl (25)
Hi ! I have troubles understanding how destructuring works in clara. If I understand the documentation correctly, I would expect this rule to match any record of type Foo
and bind the symbol foo
to the record itself, so I can use it afterward.
(defrule do-something-with-foo
[Foo [foo]]
=>
(do-something-with foo))
I always get an Unable to resolve symbol: foo in this context
, where context is the RHS.
Am I missing something ?@ggaillard the destructured bound symbols are scoped locally to the expressions of the single condition doing the destructuring bind
You have to bind a variable to use throughout other conditions or the rule and/or the RHS
This would make more sense
(defrule do-something-with-foo
[?foo <- Foo]
=>
(do-something-with ?foo))
Oh I get it ! Thank you !
times like:
(defrule do-something-with-foo
[Foo [{:keys [x y]}] (< 10 x) (= ?y y)]
=>
(do-something-with ?y))
are where it may be more usefulAlso note that when using Clojure records (i.e. defrecord
classes) and/or, on the JVM side, Java POJOs adhering to the Java bean spec, Clara automatically makes the field names visible in the local scope of a condition’s experssions
(defrecord Foo [x y])
(defrule do-something-with-foo
[Foo (< 10 x) (= ?y y)]
=>
(do-something-with ?y))
Would work without the explicit destructuring syntaxI was using your last example until now to extract my records' fields, and since I missed the <-
part of facts expressions it quickly became inconvenient when I needed to work with every fields … Thanks for this clarification 🙂
@dave.dixon that is surprising to me
@dave.dixon I think there is some sort of compilation issue happening with that rule
(defrule lost-game
[?ships <- (acc/count) :from [Ship]]
[?destroyed <- (acc/count) :from [Destroyed]]
[:test (do (= ?ships ?destroyed))]
=>
(insert! (->Loser)))
@mikerod Thanks. I had several puzzling cases like this, will try the work-around when I get home
If you look at the rulebase
for the network without the do
(the problem case), the TestNode
is a beta root with no parents
This made me suspicious of the function clara.rules.compiler/sort-conditions
which does a topological type of sort of accumulators that rearranges some conditions
The topo sort is supposed ensure that any used variable binding is pushed below it’s creation unification binding condition
Adding the do
in the :test
node pushes it down as a child of the accumulate nodes in the network and then it works
It may be completely a problem with the sort, as the below demonstrates
(ns test.ns
(:require [clara.rules :as r]
[clara.rules.accumulators :as acc]
[clara.rules.compiler :as com]))
(defrecord Ship [])
(defrecord Destroyed [])
(defrecord Loser [])
(r/defrule lost-game-fixed
[?ships <- (acc/count) :from [Ship]]
[?destroyed <- (acc/count) :from [Destroyed]]
[:test (do (= ?ships ?destroyed))] ;; Notice the `do` surrounding form is important for the sort
=>
(r/insert! (->Loser)))
(r/defrule lost-game-broken
[?ships <- (acc/count) :from [Ship]]
[?destroyed <- (acc/count) :from [Destroyed]]
[:test (= ?ships ?destroyed)] ;; Condition with problem, doesn't work without surrounding form (`do` above)
=>
(r/insert! (->Loser)))
(com/sort-conditions (-> lost-game-fixed :lhs))
(comment
({:accumulator (clara.rules.accumulators/count), :from {:type test.ns.Ship, :constraints []}, :result-binding :?ships}
{:accumulator (clara.rules.accumulators/count), :from {:type test.ns.Destroyed, :constraints []}, :result-binding :?destroyed}
{:constraints [(do (= ?ships ?destroyed))]}))
(com/sort-conditions (-> lost-game-broken :lhs))
(comment
({:constraints [(= ?ships ?destroyed)]}
{:accumulator (clara.rules.accumulators/count), :from {:type test.ns.Ship, :constraints []}, :result-binding :?ships}
{:accumulator (clara.rules.accumulators/count), :from {:type test.ns.Destroyed, :constraints []}, :result-binding :?destroyed}))
seems to be that classify-variables
considers it an equality-expression?
, resulting in them being considered "bound", but "unbound" in the workaround case
and when they are bound, this fn https://github.com/cerner/clara-rules/blob/master/src/main/clojure/clara/rules/compiler.clj#L790 will consider it satisfied because #{}
is a subset of #{?ships ?destroyed}
, meaning newly-satisfied
will contain the test constraint (https://github.com/cerner/clara-rules/blob/master/src/main/clojure/clara/rules/compiler.clj#L797) and be put in the front of the order in the into
at the end https://github.com/cerner/clara-rules/blob/master/src/main/clojure/clara/rules/compiler.clj#L828
That fixed it, as well as the other cases, which were essentially the same, with an equality check inside of [:test]
.
The compilation of this rule does seem to be wrong. When I inspect the rule network the :test condition seems to completely not exist in the network. Gist with the pretty-printed network: https://gist.github.com/WilliamParker/a3adaa3db730229bc01586009bb7178a
However, if I use join-filter-equals it is there: https://gist.github.com/WilliamParker/347ba6ca3f7e0afc2c9f183e0731111e
(defn join-filter-equals
"Intended to be a test function that is the same as equals, but is not visible to Clara as such
and thus forces usage of join filters instead of hash joins"
[& args]
(apply = args))
it is a helper function for Clara’s testsClara has special cases around constraints having = as the first symbol in order to create variable bindings, but when the first symbol is something else if becomes a “normal” call to clojure.core/=. @zylox is probably on the right track here with looking at different paths depending on whether equality-expression? returns true
I’ll need to look more closely at it to have an informed opinion on the exact cause and what to do about it but this does look like a bug to me @dave.dixon You should be able to avoid it for the moment with strategies that prevent = from being the first symbol in a :test constraint for the moment though
I logged https://github.com/cerner/clara-rules/issues/357 for this @dave.dixon @zylox @mikerod
the only thing maybe worth adding is my comment showing how the sort conditions comes out incorrect