Fork me on GitHub
#clara
<
2020-01-21
>
matthew.pettis03:01:14

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...

matthew.pettis03:01:06

My two files which fails (I"m looking only at importing rules for this examples, not yet facts):

matthew.pettis03:01:40

The session namespace...

matthew.pettis03:01:13

The rule namespace I want to import from...

matthew.pettis03:01:27

The error I get:

ethanc03:01:41

@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.

matthew.pettis03:01:19

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...

matthew.pettis03:01:41

... and that's when I run the final let that creates the session...

matthew.pettis03:01:21

oops, I take that back. When I run that require statement, I get a ClassNotFound exception for clara-lein.ex06.Myfact.

matthew.pettis16:01:24

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...

mikerod16:01:48

@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

mikerod17:01:02

You can have things in as many ns’s as you want.

mikerod17:01:27

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.

matthew.pettis17:01:01

@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

mikerod19:01:12

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.

mikerod19:01:09

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.

mikerod19:01:59

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.

matthew.pettis21:01:57

OK, this is super-helpful, thank you so much! I will try these changes as soon as possible.

matthew.pettis21:01:39

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.

mikerod21:01:09

@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.

mikerod21:01:22

Often there are solutions that don’t need that sort of feature at least.

matthew.pettis01:01:56

I agree; I don't need dynamic rules. I more needed a hint as to the fact that they are supplied and loaded upon mk-session.

matthew.pettis01:01:14

To that end, I tried your advice, I think I did it faithfully, and wasn't able to make it work yet. My adjustments are at this commit: https://github.com/mpettis/clara-ns-ex/tree/0bdb72e98a8ef657de08579af6ee993c9008a738

matthew.pettis01:01:29

When I run that, I still get that Myfact class cannot be found. I think I'm probably missing something about using defrecord definitions from other namespaces.

matthew.pettis02:01:04

Also, for a baseline, this is how I make it work when everything is in the same namespace... https://github.com/mpettis/clara-ns-ex/blob/nonamespace/src/clara_ns_ex/core.clj

mikerod02:01:59

@matthew.pettis so you’re issue isn’t just referring to record symbols. Have to follow normal clojure symbol resolution rules.

mikerod02:01:12

So you require the ns that defines the records - to ensure it is compiled.

mikerod02:01:46

Then you typically would use :import in clj (not cljs) to refer to the record class name as the shortened version

mikerod02:01:16

Otherwise you’d have to fully qualify it each reference clara_ns_ex.facts.Myfact

matthew.pettis02:01:22

Thanks. I thought about that a bit more after I posted, I'll think a bit more carefully about resolution rules, and think I may get it... Thanks for the help; learning Clojure as I learn Clara here...

mikerod03:01:55

No problem. Let me know if more questions

mikerod03:01:12

I’d give better examples but typing on mobile is hard hah

matthew.pettis03:01:34

I really appreciate the help.

matthew.pettis04:01:34

fyi, i think i'm closer, but I can't decipher the location of where the error of can't find Class for Myfact, as it won't tell me at what line it is failing. This is the state that I have it: https://github.com/mpettis/clara-ns-ex/tree/f3d6f67346eacb61294205f6da0e1f3073e7428a

matthew.pettis04:01:29

I have the import statements, I tested that it worked. I referenced these pages that went with the hint that @mikerod gave me on using import: https://danielcompton.net/2016/05/04/requiring-records-clojure-clojurescript and https://stackoverflow.com/a/23420084/1022967

matthew.pettis04:01:52

OK, I got it to work by playing with what did and did not need namespace qualified names, and where. I don't fully understand the rules of when I should and should not yet, but I have a working example to go off of, so thanks for all of your help! Here is the commit that has a working example: https://github.com/mpettis/clara-ns-ex/tree/d077c141d305ef217043bcf44a328c6921311847

wparker09:01:27

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.

wparker09:01:31

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.

matthew.pettis12:01:24

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.

mikerod13:01:22

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

matthew.pettis14:01:02

I just discovered 'how to ns' by Sierra, reading that to guide me on that: https://stuartsierra.com/2016/clojure-how-to-ns.html

mikerod14:01:55

I thought it had some good points. I can’t remember if I fully agreed with it, but analyzing require forms can become a debate of “taste” at a certain point. Hah. He does at least mention its opinionated. 😎

matthew.pettis14:01:53

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