clara

http://www.clara-rules.org/
Matthew Pettis 2020-07-29T00:37:45.249300Z

Are there examples of using things like multiple insert! calls, or insert! inside of an if-let on the RHS of a rule? Or reasons why that isn't allowed? I'll try once back at a computer, but I wanted to ask in case there was something obvious I should know.

ethanc 2020-07-29T13:43:00.254Z

The rhs of a rule is inside an implicit let block, so it should allow for multiple inserts:

(defrule rule-name
  <lhs>
  =>
  (insert! <fact1>)
  (insert! <fact2>))
https://github.com/cerner/clara-rules/blob/main/src/main/clojure/clara/rules/compiler.clj#L454-L456 The RHS can be arbitrary clojure code, so if-lets would be fine. Though as a word of warning, don’t do stateful things in the RHS of a rule as it can lead to weird side effects.

👍 2
Matthew Pettis 2020-07-29T13:50:55.255100Z

Thanks! Warning taken. I'm trying to put together a framework like the 'sensors' example in the https://github.com/cerner/clara-examples repo. I haven't worked out how I want it structured yet, but I know I want to trigger using a function to compute something based on the presence of facts, and then conditionally insert another fact based on what that function returns. Something like, "if these two facts exist, do this computation on them. If that computation yields a certain result (like a constraint violation) insert a fact that records the constraint violation. Otherwise, don't insert a fact." I'm not sure if good practice is to that computation on the LHS of the rules as a precondition, and then the RHS will trigger if result passes a test, or to do that check totally on the RHS.

Matthew Pettis 2020-07-29T13:59:06.256Z

Here's some pseudocode of what I am trying to decide between, not sure if both are even feasible or, as noted here, advisable:

Matthew Pettis 2020-07-29T13:59:11.256200Z

;; pseudocode for a lhs computation.  I'm not sure how to compute ?c here syntactically, if possible at all.
(defrule lhs-pseudocode
 [?a <- ConditionA]
 [?b <- ConditionB]
 ;; [?c <- compute a function return value (check-function ?a ?b)]
 ;; [:test ?c is truthy]
 =>
 (insert! (->ExceptionFact ?c)))


;; pseudocode for a rhs check
(defrule rhs-pseudocode
 [?a <- ConditionA]
 [?b <- ConditionB]
 =>
 (let [c (check-function ?a ?b)]
  (when (truthy c)
   (insert! (->ExceptionFact c)))))

2020-07-29T14:05:26.257200Z

I’d add it’s generally not good to do conditional things in RHS. The rules themselves are meant to capture the conditional flows

2020-07-29T14:05:43.257700Z

I generally find it an anti pattern for the RHS to “maybe do some insert”

2020-07-29T14:05:47.257900Z

There are exceptions

2020-07-29T14:06:01.258300Z

But above , you’d be better off using a :test node on LHS

Matthew Pettis 2020-07-29T14:14:27.260300Z

Thanks, makes sense. My question then is: If I do a test, and in that test, I use a function to compute a value which is to be checked, and I want to pass that value on to the RHS as part of the fact insert, what would that look like? In the first pseudocode above, I want to pass the output value of (check-function) to be inserted as a field in a record on the RHS...

Matthew Pettis 2020-07-29T14:19:09.262400Z

The one way I thought to get around that was to not check conditions at all in the rules, but always insert a fact with the results of the computation I want to do, and that record has the result. And then, during a query, I can filter out those new records based on some flag/indicator field of the result. That seems like I potentially put more facts into the session than are necessary.

Matthew Pettis 2020-07-29T14:20:40.264Z

Another thought I was to follow up what I did above, but then add another rule that prunes facts that don't have a certain value in an indicator field -- like a cleanup rule. Then there is no conditional inserts in the LHS or RHS of a rule, but another rule takes care of the conditional inserts by removing things that I would not have originally inserted.

Matthew Pettis 2020-07-29T14:21:25.264800Z

Again, new to this, so I am trying to map the ideas of how I want my conditional domain logic I want to the best practices and discipline of a rules system...

2020-07-29T17:18:52.265600Z

@matthew.pettis I’d recommend a rule that always inserts facts modeling result values you want like that. You sort of already said this above

2020-07-29T17:19:14.266300Z

So the answer is just more rules. More “intermediate facts” representing knowledge of the system

2020-07-29T17:20:53.266900Z

I can’t write up too many useful details at the moment though. So I may not be that useful to you

2020-07-29T17:21:21.267600Z

If you make queries more specific. You don’t have to worry as much about adding more intermediate level facts though

2020-07-29T17:21:49.268500Z

And don’t typically need cleanup. Unless you have some memory concern or something. That can be somewhat trickier to retract things in a way that plays well with truth maintenance

ethanc 2020-07-29T17:59:19.268600Z

As mike has mentioned(outside of this thread) LHS is probably the ideal place for this sort of logic. We can continue that conversation there. :)

👍 1
Matthew Pettis 2020-07-29T18:05:03.270500Z

@mikerod Thanks! The more I thought about my proposed alternatives, the more it seemed to be in line with the rules philosophy. I'll be trying that. I appreciate the thought of writing something up too. I think I have enough to go on to code to this philosophy to start with, and I'll probably have other questions later. Thanks again!

Matthew Pettis 2020-07-29T18:24:49.270700Z

Thanks! Yep, Or, for my first pass, with my logic, I'm going to not do conditional inserts within rules -- I'm going to try to just do do inserts with a record of a natural structure that allows me to pull out what I want on a query, and put logic in the query, or even post-process query outputs. Thanks for the help!

Matthew Pettis 2020-07-29T20:48:03.272200Z

Also, from a thread way-back... I'd like to try clara.tools, I see the repo here: https://github.com/rbrush/clara-tools ... what I don't see is the :dependencies version string to put into my project.clj file... and advice on how to install from this repo?