This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-08-29
Channels
- # aws (1)
- # beginners (78)
- # boot (27)
- # cider (16)
- # clara (15)
- # cljs-dev (84)
- # cljsjs (13)
- # cljsrn (19)
- # clojure (65)
- # clojure-france (10)
- # clojure-italy (8)
- # clojure-russia (35)
- # clojure-spec (34)
- # clojure-uk (124)
- # clojurescript (50)
- # clojutre (3)
- # core-async (16)
- # data-science (18)
- # datascript (1)
- # datomic (9)
- # emacs (2)
- # flambo (3)
- # fulcro (55)
- # graphql (3)
- # hoplon (4)
- # jobs (2)
- # juxt (21)
- # keechma (6)
- # lumo (73)
- # off-topic (4)
- # om (10)
- # onyx (5)
- # parinfer (1)
- # pedestal (3)
- # re-frame (60)
- # reagent (19)
- # specter (24)
I have a shared entry point into a rule session where a namespace associated with specific rules/queries can be applied:
(defn run-rules
"Runs a rule session given a rule namespace, a root state tree node, and a performed action."
[ns root action]
(let [session (-> (r/mk-session 'justice.rules.shared ns)
(r/insert (->Tree (walk/prewalk tree/augment-if-node root))
(->Action action))
(r/fire-rules))]
...
However, I get a No namespace
exception when I pass a namespace in eg. (run-rules 'justice.rules.x ..)
. I've "fixed" the issue by adding (require ns)
inside run-rules
, but is there a better way to accomplish this? I could for example just require the namespaces in the ns
declaration, or I could have a whitelist of namespaces to require, but I'd like to keep adding new namespaces to be as simple as creating a new namespace and passing it in to the generic shared entrypoint.> Thanks! I was struggling because for this specific problem coming up it was not clear how to create facts that are [:not [B (= ?id id)]]
@iku000888 sorry for long delay, just saw your response. I’m not sure what you mean to create a fact to match a negation (ie condition with a :not
). The negation condition is used in conjuction with the [A (= ?id id)]
in my example. This together forms a “join” sort of query for all A
that do not have a matching B
. So back to that example, repeating for clarity:
(r/defrule joins
[A (= ?id id)]
[B (= ?id id)]
=>
(r/insert! (->C)))
(r/defrule nil-joins
[A (= ?id id)]
[:not [B (= ?id id)]]
=>
(r/insert! (->C)))
The rule names may not be the best, but sticking with them:
* The rule joins
is for the typical “inner join” matches - it matches A
with B
s they go with
* The rule nil-joins
is for the “outer left” part of the join - it keeps the A
results that do not have any actual join with a B
This may not be the specific issue you had, but that was my interpretation of your “left-join” type of rule question.
Given that you have something with retraction working, I’m thinking this is a different problem you are wanting to solve.I’m not positive that would be a great idea either. Best practice in clj would be to be sure to appropriately declare the namespace you want to have compiled - most preferably in the ns
header :require
s
You could get away with a dynamic require
call in your run-rules
though. I’ve had use-cases doing that before to load rules from arbitrary namespaces
I don’t think there is a “better way to accomplish it” though in terms of what Clara currently offers.
@mikerod thanks, this would be regular clojure. I've moved away from dynamic require, as it was adding unnecessary compilation overhead (in terms of performance). I'm still using a ns-whitelist var though rather than using the (ns .. (:require ..))
form as I've had some formatters remove the references as they are "unused".
Yeah, if you wait to do a require
in the fn, it could be a pretty big slowdown depending on the size of it
and perhaps could be better suited at “compilation time” with whatever else you have to start up the app
Not to get too lost in the weeds, but Clara does have a few other alternatives instead of loading whole ns’s of rules
For example, you can provide collections of rules to r/mk-session
(defrule r1 <etc>)
(defrule r2 <etc>)
(defrule r3 <etc>)
(def rule-group1 [r1 r2 r3])
(defrule r4 <etc>)
(defrule r5 <etc>)
(defquery q1 <etc>)
(def rule-group2 [r4 r5 q1])
(defrule r6 <etc>)
<....>
(r/mk-session rule-group1 rule-group2 [r6])
@dadair Agreed with @mikerod ‘s previous comments. I wanted to add that namespace compilation can have arbitrary effects in Clojure; one notable one is protocol extensions. That is, a linter removing namespace whose vars you aren’t directly using from your requires could be dangerous to things other than Clara. Also, note that require will short-circuits if the namespace already exists unless you specifically request reloading, so the performance question is really whether you pay the cost at initialization of your Clojure environment or the first time you build the relevant session.