[rules-users] Drools and Clojure

Mark Proctor mproctor at codehaus.org
Wed Aug 12 00:09:16 EDT 2009


Map( this['c'] == 206 )

That should work, we do support MVEL syntax for maps and arrays - we 
just don't suppor method calls, yet.

Mark
André Thieme wrote:
> My previous mail did not really have any resonance, which is probably
> due to the fact that I put the word "Clojure" into the subject. Some of
> you may not know that Clojure is a programming language which compiles
> to Java byte code and which can use all Java classes and which can
> itself also produce classes which are then usable in Java.
> I hope this time some people will answer :-)
> I would like to see some comments about the eval+Maps issue and a tip
> how I can have rules which are not constraints at the same time about
> what can be inserted into a session.
>
>
> Currently I am writing a lib which will make Drools usable from within
> the Clojure programming language. That is good for both, Drools and
> Clojure. That way Drools gets more users and more Clojure users can use
> that powerful system.
>
> At the end of this mail you will find my original one that I sent some
> days ago which got no answer. By now I found out the answer myself.
> My question was if I can write rules which will fire when some specific
> key/value pairs are set in a HashMap. And yes, I was able to do this via
> eval.
>
> Is there really no other way than using eval?
> The disadvantage seems to be that Drools can not do optimizations over
> evals, but needs to execute that code each time.
> In Clojure it is typical to use Maps instead of POJOs. That would mean
> that Clojure users either have to do non-idiomatic programming, or
> accept that their rules will be slower.
> Although in principle maps and class instances are equivalent. POJOs are
> also maps. They map a key, the field of the class, to a value. POJOs
> have one implicit field, which is their type.
> We could have a
> class Person {
>    String name;
>    int age;
>    ...
> }
> or a Map with just three fields: name, age and type.
> In the type slot we could store the string "Person".
> Clojure Maps are immutable which should allow even more optimizations
> than we can have in Drools from POJOs.
> Anyway, here is a rule that I currently use:
>
> package droolsandclojure;
> import clojure.lang.APersistentMap;
>
> rule "Clojure test 1"
>   when
>    m:APersistentMap()
>    eval((Integer)m.get("c") == 206)
>   then
>    System.out.println("Match: " + m);
> end
>
> and in my Clojure program I create a stateful session object and then
> (.insert session {"a" 1, "b" 2, "c" 3})
> (.insert session {"a" 1, "b" 2, "c" 206})
> which inserts two maps into the session object.
> When I then (.fireAllRules session) then it prints out
> Match: {"a" 1, "b" 2, "c" 206}
> as expected.
> One thing is strange though: I can not insert Maps which don't have a
> key "c" - I get a NullPointerException.
> It seems that I set a constraint of which things can go into a session
> instead of having a rule which fires only for those Maps that come with
> the right key/value pairs. Btw, this also happens when I use a
> java.util.HashMap instead of the Clojure one.
>
> The rule then looks like:
> package droolsandclojure;
> import java.util.Map;
>
> rule "Clojure test 2"
>   when
>    m:Map()
>    eval((Integer)m.get("c") == 206)
>   then
>    System.out.println("Match: " + m);
> end
>
> And I do
> (let [map (new java.util.HashMap)]
>    (.put map "a" 10)
>    (.put map "b" 20)
>    (.put map "c" 206)
>    ...
>    (.insert session map)
>    ...)
>
> which then prints:
> Match: {b=20, c=206, a=10}
> But if I don't (.put map "c" 206) but instead (.put map "d" 206) then I
> get the NPE.
>
> Here my earlier original mail which you can safely ignore.
>
>
> Greetings,
> André
>
>
>
>
> André Thieme schrieb:
>   
>> Hello group!
>>
>> I am a Clojure user and would like to look into using Drools with it.
>>  From Clojure I can use all Java classes and call all methods. So,
>> instantiating a KnowledgeBase, KnowledgePackages or a KBFactory is no
>> problem, and calling the respective methods, to get the system started,
>> or insert facts into a session, dispose it and fireAllRules is also
>> fine.
>>
>> It is just that in Clojure one typically does not use POJOs.
>> The most typical data structures used are: hashmaps, vectors, structs,
>> lists, structuremaps and sets (in no particular order).
>> Those are persistent and "concurrency ready" Clojure DSs, and btw, also
>> usable from Java directly.
>>
>> Now the basic Drools examples I saw all work with POJOs.
>> They were mostly comparisons of native typed fields in a class.
>> I however would be interested to compare different qualities of key/
>> value pairs in a hashmap, or compare structs with each other.
>> Comparing structuremaps is maybe what comes closest to the POJO examples.
>> For example, we may have a struct (defstruct person :name :age :type)
>> and store it's instances in a vector. And we would also insert it into
>> our Drools session object:
>> (doseq [p all-persons] (.insert session p))
>> which would correspond roughly to
>> for (Person p : allPersons) { session.insert(p); }
>> Only that "person" is a map, not a class.
>> Well, of course it is also an object and under the hood a class, a
>> java.util.Map even - but I mean that unlike POJOs the key/value pairs
>> are stored differently (fields in classes are also just key/value pairs).
>>
>> So, is there a way to insert maps (let it be java.util.HashMaps for
>> example) and also to reason about them? I won't care if the syntax
>> for that is complicated. As Clojure is a Lisp I would just write a
>> little macro and have a very nice and readable syntax (probably a bit
>> similar to the one of Jess or Lisa).
>> Instead of looking at all instances of the type Person AND then have
>> their age compared, I would look at all maps/structs which have a key
>> :type with the value :person AND then compare the value of their :age
>> key with a number, such as:
>> (rule "example person rule, matching non-adults"
>>    :when (= :type :person)
>>      (< :age 18)
>>    :then
>>      (println "No service for minors."))
>>
>> In Java terms it would mean that we insert several instances of
>> java.util.HashMap, all having the k/v pairs for name, age and type.
>> HashMap<String, Object> hm = new HashMap<String, Object>();
>> hm.put("name", someName);
>> hm.put("age", someAge);
>> hm.put("type", "person");
>> session.insert(hm);
>>
>> Is that in principle possible with Drools 5?
>>
>>
>> Sunny greetings,
>> André
>>     





More information about the rules-users mailing list