[rules-users] Drools and Clojure

Greg Barton greg_barton at yahoo.com
Tue Aug 11 21:24:59 EDT 2009


There's no reason why a rete based system couldn't use maps as first class objects, but Drools is heavily oriented towards POJOS.  Using eval in the way you have is pretty much the way to go.

That being said, a possible direction for Drools might be the way the object-relational mapper Hibernate handles things: tuplizers.  When you create a Hibernate session you can specify how you want it to instantiate the data mapped in the database.  There are three built in tuplizers: Dom4jEntityTuplizer, DynamicMapEntityTuplizer, and PojoEntityTuplizer.  (And you can create custom ones.)  

As long as type information is accessible (both for first class types and their members) you should be able to have the left hand side of a rule (the conditions) be as it is now.  The right hand side is another story entirely, as Drools does no processing on it whatsoever.  Creating new working memory objects could not be generic, and neither would modification of existing objects.  If you did want to make it generic, you would lose the full power of java by handing working memory object creation and modification over to Drools.  In my experience when systems try to take on that responsibility they don't do it as well, or as flexibly, as java does.

If you lobby the devs hard enough and get others on your side you may be able to convince them to go in that direction, but I doubt it would be possible before version 6 or so, if that early.  (And I'm not even sure it's possible.)

--- On Tue, 8/11/09, André Thieme <address.good.until.2009.dec.14 at justmail.de> wrote:

> From: André Thieme <address.good.until.2009.dec.14 at justmail.de>
> Subject: [rules-users] Drools and Clojure
> To: "Rules Users List" <rules-users at lists.jboss.org>
> Date: Tuesday, August 11, 2009, 5:01 PM
> 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é
> -- 
> Lisp is not dead. It’s just the URL that has changed:
> http://clojure.org/
> _______________________________________________
> rules-users mailing list
> rules-users at lists.jboss.org
> https://lists.jboss.org/mailman/listinfo/rules-users
> 


      




More information about the rules-users mailing list