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(a)justmail.de>
wrote:
From: André Thieme
<address.good.until.2009.dec.14(a)justmail.de>
Subject: [rules-users] Drools and Clojure
To: "Rules Users List" <rules-users(a)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(a)lists.jboss.org
https://lists.jboss.org/mailman/listinfo/rules-users