Fork me on GitHub
#clara
<
2018-08-08
>
jeremy64221:08:48

@mikerod Do you know if there is a way to go from Java -> Clojure when rules are keywords and not records/java beans?

enn21:08:03

I have a Clara rulebase that, for comprehensibility reasons, I’d like to split into “phases.” So, phase 1 gets a bunch of facts, the rules fire, and new facts are generated. Somehow, this becomes the input to phase 2, which fires a new set of rules, generating new facts. Etc. I’m not sure what the best way is to pass the output of one phase into the next. Originally I was thinking I’d just take the session from phase 1 and use that as the starting point for phase 2. But unless I’m missing something, it’s not possible to load new rules into an extant session. So I guess what I need to do is to just grab all the “output” facts out of phase 1 and insert them into a new session for phase 2. Is there a simpler way to do this than by writing (and then executing) a query for each and every fact type?

mikerod21:08:01

@enn > So I guess what I need to do is to just grab all the “output” facts out of phase 1 and insert them into a new session for phase 2. Is there a simpler way to do this than by writing (and then executing) a query for each and every fact type? I think this is likely a smoother approach (there is an alternative I can mention in a min) As far as worrying about querying for too many types, maybe you can make a “parent” type for the output types you care about and just have a query on that parent type.

mikerod21:08:05

Another possibility is to just use one session with all the rules. I’m not sure what about your logic makes it where you’d want some rules to be separate from others. Are you worried about them running too early or something, performance?, etc

mikerod21:08:32

I don’t understand your situation enough, but there is something called “activation groups” that can also be considered in some cases that may relate to yours https://github.com/cerner/clara-rules/blob/0.18.0/src/main/clojure/clara/rules.cljc#L328-L331

enn21:08:07

No, they’re all in one session now and it works fine performance-wise. It’s more about making it easier for the programmer to understand the universe of rules and fact types in play at a given point in the code.

enn21:08:11

And making it easy to ensure that an intermediate fact type (used to derive some other final result) does not get inadvertently referenced outside of its intended scope

enn21:08:35

Maybe my whole idea is antithetical to the idea that a rules engine hides some of the order-of-execution concerns of regular code? I’m not sure.

mikerod21:08:21

Yeah, I understand your reasoning then

mikerod21:08:30

You can separate them to independent rulesets if that helps reasoning

mikerod21:08:52

there would be a slight cost of taking facts from queries of one session and inserting again into another session, but that may not really be a concern

mikerod21:08:09

but yeah, order independence is a key idea

mikerod21:08:26

and I’d also sort of argue that rules should be able to somewhat function in aggregation with one another

mikerod21:08:48

where they can all share related information when relevant, but they are also fine when the information they need isn’t present etc

mikerod21:08:45

> And making it easy to ensure that an intermediate fact type (used to derive some other final result) does not get inadvertently referenced outside of its intended scope I think this is the most interesting concern though.

mikerod21:08:20

If everything gets lumped together, I can see how it could become problematic if you were wanting to have a better control on scope

mikerod21:08:16

@jeremy642 I don’t think I understand the question. You want to use rules with “keyword” sort of type dispatch, but on underlying Java objects? Maybe show a dummy example or something.

jeremy64221:08:25

I have rules built on a keyword type dispatch. I wasn't sure if there might be a way to create them from Java.

jeremy64221:08:57

More a question on Java -> Clojure interop I guess than clara-rules. How I might go about creating the necessary type map for clara-rules from Java.

mikerod21:08:42

@jeremy642 you can provide a custom fact-type-fn (and ancestors-fn if relevant) http://www.clara-rules.org/docs/fact_type_customization/

mikerod21:08:34

This may be what you are looking for. e.g. the fact-type-fn could take an arbitrary (Java) object and map it to some keyword/alternative typing system you have within the rules

mikerod21:08:53

A likely incomplete example, but perhaps inspiration to think about.

mikerod21:08:25

I’m not sure if you already have a custom fact-type-fn or not though. You might since I see you have a map with :weather-fact-type

mikerod21:08:45

Clara’s default is clojure.core/type, which is implemented as (or (get (meta x) :type) (class x))

jeremy64221:08:06

Yeah I was just thinking of a way to take Java Maps and Convert it to Keyword Key Map in the fact-type-fn?

mikerod21:08:30

depending on what is priority, you may flip the if above and do alternative things. I was just showing the idea of what could be done at least.

jeremy64221:08:31

Currently the custom fact-type-fn looks for :type or something like that. I'll have to grab the work laptop. This just came to me off the top of my head.

mikerod21:08:03

so for the fact-type-fn you just need to return the type (a keyword in your case I think)

mikerod21:08:30

but if you want to also work with the Java data as clj maps, like this: (< (:temperature this) 0), you’d have to convert the facts themselves coming into the session

mikerod21:08:40

which is different than the fact-type-fn

jeremy64221:08:05

Yeah that's how the rules work with them currently.

mikerod21:08:05

the fact-type-fn doesn’t transform the facts that are used during constraint matching. It just gives a value that is used as the “fact type” partitioning of the network

mikerod21:08:34

I’ve wrote Clara rules a bunch with external Java type data

mikerod21:08:00

What I’ve done typically is to have some fairly basic rules to match those types and apply any sort of useful filtering if performance was a conern

mikerod21:08:13

but then just had the RHS insert a clj type to use in all the rest of the rules

mikerod21:08:32

so the rule was sort of a transformation/mapping from Java to CLJ data structures

jeremy64221:08:07

Ahh, might be a decent idea.

mikerod21:08:05

(defrule find-java-things
  [JavaThing (= x ?x)]
  =>
  (insert! {:fact-type :thing
            :x ?x}))

mikerod21:08:19

but if you need the JavaThing in the end, for the query consumer

mikerod21:08:23

you can attach it to the facts

mikerod21:08:30

either as a first-class field, or metadata

jeremy64221:08:43

I was thinking about keeping the rules in a seperate jar. And allowing Java applications and Clojure applications import and use the rules in that jar. The question was just how to define the facts when the rules were all in Clojure and no beans were provided that allowed Java to create them.

mikerod21:08:51

(defrule find-java-things
  [?j <- JavaThing (= x ?x)]
  =>
  (insert! (with-meta {:fact-type :thing
                       :x ?x}
             {:source ?j})))

mikerod21:08:23

If it’s a classpath issue, where you won’t know the types ahead of time you’ll have to be more tricky

mikerod21:08:38

and also, you are going to hit reflection stuff I’d think that way

mikerod21:08:28

or you write your core set of rules in a type-independent rule set/session

mikerod21:08:19

and you have to write a separate ruleset that does know about specific type mapping parts - or maybe you don’t need rules for that transformation layer at all.

jeremy64221:08:47

I thought about just writing a interface for Java to call and create facts, insert into engine. But the rule engine itself would most likely need to be run in clojure and not instantiated from Java so it's pretty self contained. It'd save me the hassle of dealing with java vs clojure types.

jeremy64221:08:19

Java doesn't really need much control over the engine. Just to insert facts and get response from the queries.

mikerod22:08:42

yeah, hard to say without fully understanding the setup you have