Are there any examples out there that show how to define a session in the namespace project.a
, define facts in namespace project.b
, rules in project.c
, and how to import b
and c
into a
to fire rules on them? I'm struggling because I"m sure I don't have namespace usage well...
My two files which fails (I"m looking only at importing rules for this examples, not yet facts):
The session namespace...
The rule namespace I want to import from...
The error I get:
@matthew.pettis I believe you would want to import the fact into the namespace where the rule is using it:
(ns clara-lein.ex06rulesns
(:require [clara.rules :refer :all]
[clara-lein.ex06 :as ex06])
(:import [clara-lein.ex06 Myfact]))
;; Rules namespace for rules for ex06.
;; Define rules
(defrule rule-1
"Rule-1 in rulesns"
[Myfact (= ?size size)]
[:test (> 1 ?size)]
=>
(insert! (ex06/->Rulefail "rule-1" ?size)))
Though this might lead to issues with cyclical dependencies, with the session ns and rules ns.
It might be practical to pull the fact definitions out into a separate ns so that they can be pulled by both ns without the possibility of a cycle.Thank you! If I copy and pasted this correctly, when i run (require 'clara-lein.ex06rulesns)
in clara-lein.ex06
, I get an error that No namespace: clara-lein.ex06rulesns found
. I'll keep poking, but that's my current error...
... and that's when I run the final let
that creates the session...
oops, I take that back. When I run that require statement, I get a ClassNotFound exception for clara-lein.ex06.Myfact.
So, I was able to make this work if I included all of the defrecords, rules, queries, and facts in a different namespace, referred that namespace, and then made a session with mk-session 'source.namespace)
. However, I am struggling to modularize this, so that I can make a session where facts come from one namespace, rules from another namespace, and queries from yet another namespace. I'm not sure if it is the namespace inclusion-ing that I am messing up, or that I don't know the proper syntax on mk-session
or insert
to build up a session that has facts, rules, and queries from these sources in it. An pointing to examples of rules that has that would be appreciated. I am doing this as an exercise to show that you can make a modular rules system and that clara-rules can support that modularity. Which is why I'm making a toy example that is doing this... thanks in advance...
@matthew.pettis there are no special evaluation rules being done by Clara. You have to appropriately require all ns’s you depend on in the order they depend on each other via standard clj :require
You can have things in as many ns’s as you want.
If you show your example I maybe can help. I’m confused what you are using to get into a situation you are describing at this point.
@mikerod Thanks for the help! I pushed a repo here of example code that doesn't quite work, but has the rules, facts, and queries in separate namespaces. The part is at the end of the core.clj file, where I assemble a session out of the facts, rules, and queries, to be able to query the session. https://github.com/mpettis/clara-ns-ex
Looks like you've got it figured out mostly. :). You don't need the fact class imports in your core namespace, but rather just in the rule/query namespaces that use them. Clara rule and query defs use the same evaluation semantics as normal Clojure code, that is something used, be it a function, class, etc. needs to be resolvable in the namespace where the rule/query is being defined. Also it's somewhat a matter of personal preference, but I find it more clear where things are coming from when I use :as aliases rather than :refer :all, that might help reduce confusion.
Admittedly there's a fair amount of code in Clara dealing with that evaluation, but the end result is to have the same user-facing semantics.
Awesome! I'll take out the require and import statements in core and use :as aliases in the rest and push a cleaner example. Thanks for the help.
I also advocate for :as aliases too. It helps to make it clear where symbols are coming from. ClojureScript doesn’t even have :refer :all support! Hah
I just discovered 'how to ns' by Sierra, reading that to guide me on that: https://stuartsierra.com/2016/clojure-how-to-ns.html
OK, just for completeness and posterity... For this question, which was how to use namespaced rules, queries, and facts in making a session, I cleaned up the namespace declaration, using Sierra's "how to ns" style recommendations, and removed most :require :refer :all statements to be more precise (except for clara-rules
). Here is the commit that is cleaned up per this comment: https://github.com/mpettis/clara-ns-ex/tree/8b78de1442fd8484341cc68d6ac63b7d78cf85e4
First just style Notes:
In clara-ns-ex.core
, typically all :require
are in one spec, but I guess what you have can work? I haven’t actually tested in recent clj:
(ns clara-ns-ex.core
(:require [clara.rules :refer :all]
[clara-ns-ex.facts :refer :all]
[clara-ns-ex.rules :refer :all]
[clara-ns-ex.queries :refer :all]))
Instead of
fact-sess (apply insert query-sess facts)
use insert-all
fact-sess (insert-all query-sess facts)
just since there is API to make you not need to use apply
with varargs.Second (most important):
You don’t insert
rules and queries. These are needed upfront at mk-session
time. Clara actually does not support dynamically adding or removing rules/queries at all right now. It may be a future potential to add, but there are perf benefits to not doing so. Even if it did allow that later though, you wouldn’t “insert rules”.
insert
is meant for facts, not rules.
what you’d need to do for your example is:
init-sess (mk-session 'clara-ns-ex.core 'clara-ns-ex.rules 'clara-ns-ex.queries)
if you just wanted to automatically include all rules/queries defined in those namespaces.OK, this is super-helpful, thank you so much! I will try these changes as soon as possible.
I was not aware that rules and queries could not be dynamically added. It is nice to see an example of mk-session
with multiple namepaces put in there. I wasn't sure if it was as a varargs or as a vector of namespaces, etc.
@matthew.pettis there is likely an issue or 2 in the github repo discussing dynamic rules. However, you may not need it as much as immediately it seems.
Often there are solutions that don’t need that sort of feature at least.