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/