[rules-users] Drools as Lexer / Parser (sequential data processing)
Hello group, I recently had the idea: A rule system (like Drools) is ideal for making programs with complex rules simpler. Writing a lexer or parser can be non-trivial. So, is it possible and also meaningful to express such a task with rules? Anyone here who maybe tried that already? The two big questions for me are: 1) how easy is it to express a lexer with rules? 2) how bad (good?) will it perform? If you happen to have a good idea of how to do it, could you please give me an example for a simple lexer? Let's say it will get natural language (a string, such as this email) as input and should return a sequence (say, ArrayList) of Tokens, which may look like this: public class Token { public String value; public String category; Token(String value, String category) { this.value = value; this.category = category; } } We could have three categories: word, numeric and whitespace. An input String could be: We can see 500 cars And it should produce an ArrayList with the contents: [ Token(We, word), Token( , whitespace), Token(can, word), Token( , whitespace), Token(see, word), Token( , whitespace), Token(500, numeric), Token( , whitespace), Token(cars, word) ] At the moment I have difficulties to see if/how this could be achieved. If you find this easy, please post a solution. I am aware that JavaCC is really good for such tasks and will also perform extremly well. Greetings, André ___ rules-users mailing list rules-users@lists.jboss.org https://lists.jboss.org/mailman/listinfo/rules-users
Re: [rules-users] Maps in Drools
Edson Tirelli schrieb: Andre, The misunderstanding here is that the LHS, except for code blocks like eval, return value expressions and accumulate code blocks, are all Drools Language. When you use the dialect attribute in a rule or package you are telling the compiler what dialect (MVEL or Java) you will use inside these code blocks mentioned previously + the language for the RHS. Good that you clear that up, thanks. In other words: Map( this[type] == Point, $x : this[x], size == 5 ) I thought this is only possible when one declares the dialect MVEL. Now I understand that what you just wrote also results in a valid rule, even when MVEL is not set. In your code above you: 1. check if this.get(type).equals(Point) 2. you set $x to the value of this.get(x) 3. check if this.size == 5 where I interpret 3. as calling the size() method of java.util.Map. Was that right so far? Everything you see in the previous expression is Drools language, does not matter if you set the dialect to java or mvel in the rule. It happens that Drools uses the same map syntax as MVEL (and a lot of other scripting languages). Also, we know, that drools implementation will resolve the first 2 above expressions in MVEL behind the scenes, and the 3rd will be resolved nativelly, but that is not something users should have to worry about, since they are writing it in Drools Language. Yes, I understand. Only with the middle part I still have some problems. As you used it, $x : this[x] it works for me. A minor issue I have with this, but we can ignore that for now, is, that this will be interpreted. The real problem for me is: it only works when between the brackets there is a literal string. I however can't do that, because my lib should support the general case, where between the brackets there could be any type of object (which I would pass in via a global var). If they write an eval, THEN they need to differentiate between MVEL and Java according to the chosen dialect. rule xyz dialect mvel when eval( ...here you write MVEL code... ) then // here you write MVEL code end rule xyz2 dialect java when eval( ...here you write JAVA code... ) then // here you write JAVA code end Very good, thanks again for clearing this up. André -- ___ rules-users mailing list rules-users@lists.jboss.org https://lists.jboss.org/mailman/listinfo/rules-users
Re: [rules-users] Maps in Drools
Edson Tirelli schrieb: 2009/8/20 André Thieme address.good.until.2009.dec...@justmail.de mailto:address.good.until.2009.dec...@justmail.de Would there not be an addition to the syntax needed, for the default rule language? For mvel it would not require a change I guess. No, as I mentioned to you, the idea is for the DRL to remain the same, so that the rules author does not have to worry about what the IT guys are doing with the domain model. So, the rules author would write: Customer( name == bob ) The IT guy would simply use a configuration to tell the engine: this object type uses a map format, that one is a POJO, that other is an XML entity, etc. For instance, if he wants to do that in DRL (he could also use API, or conf files), he could do: declare Customer @format( map ) end declare Order @format( pojo ) end So, we have a clear distinction between the technical aspects and the business aspects of the application. Ah okay, that makes sense and sounds very good! So, when Maps become first class not the rules will introduce a new syntax, but intead we would add some declarations. The only two challenges I see with that is: 1. to allow the keys to be any object and not just only strings. 2. differentiate between fields of the Maps and keys Point 1.: Your example Customer( name == bob ) currently is expressed as: $m : Map(eval($m.get(type == Customer)) eval($m.get(name) == bob)) right? In the declaration for Customer above one would probably have to specify that for a Map being a Customer depends on the key type. But what would Customer( name == bob ) look like if name was not a String but some other Object? This info should go into the declare part, yes? Point 2.: in your other reply you gave the example: Map( this[type] == Point, $x : this[x], size == 5 ) Projected on the Customer( name == bob ) example we would need to differentiate, in Java terms, between: map.get(size) == 5and map.size() == 5 So Customer(name==Bob size==5) needs to be clear. Your clojure macro would generate always the same DRL code Customer( name == bob ), but you said yourself that clojure is 100% java compatible, so imagine the enterprise had a domain model implemented as pojos already and as part of the new application some new entities are modeled in clojure. The macro would generate always the same code, but you would configure some entities in the domain as POJOs and other entities as Maps. Yes, it would be similar like that. What I first do is to write a general macro named rule which should be able to express everything that currently is possible in Drools. On top of that macro I will define some more, for exmaple there could be a macro map-rule which is specialized on maps. This can get even more specialized, but in the end all those macros will expand into the general one, and the general one will generate a string S which contains all rules and then do a ResourceFactory.newByteArrayResource(S.getBytes(utf-8)) to get the rules into Drools, which will from then on continue. And the way as you suggest it, to let the Drools syntax always be the same, it may even be easier for me to write my lib. Good :) Hmm, but the MVEL syntax can not magically eliminate the eval. Under the hood the map accesses will still be inside an eval. Marc confirmed that a few days ago. MVEL only hides this from the user. This is what I will also do. But under the hood it will become $a:Map() $b:Map() eval( $a.get(type) == Customer ) eval( $b.get(type) == DailyOrders ) Here I think we have other misunderstanding. I will try to explain, but ideally you need to learn a bit about the Rete algorithm to see the whole picture. Yes, I will read soon (I hope) Chapter 12 of my Drools book, by Michal Bali. There are 2 types of eval(). Inline eval() and top level eval(). Ah, I see, I didn't know that. What you wrote above is a top level eval, meaning it will become a node in the rete network. So your example above generates an execution plan (making an analogy with SQL) that will get all Maps in the working memory, join them in tuples size 2, and then test each tuple for the 2 evals. So, you see why this will generate C(n,2) partial matches, while C(n,2) as we know is n!/(n-2)!, what is really bad for growing n. Very good, now I understand what you mean. Thanks for the sql analogy. Now, the same thing could be written using inline evals as: $a:Map( eval( $a.get(type) == Customer ) ) $b:Map( eval( $b.get(type) == DailyOrders ) ) Excellent, this is very helpful. I just tried this out and was also successful with it. Now I can have my code generating alpha constraints without having to use MVEL. In this case, the inline eval() will generate an alpha constraint in the network, i.e., it will be applied BEFORE the joins. So, instead of doing all
Re: [rules-users] Maps in Drools
Edson Tirelli schrieb: ooops... correct version: when Map( this[type] == Point, $x : this[x] ) Map( this[type] == Circle, this[x] == $x ) then end Okay, so in the mvel syntax this is possible. Can this also be achieved in the default rule syntax, without mvel? The mvel syntax needs to be interpreted at runtime, so my Clojure lib will have to output rules in Drools' native rule language. What interests me most is that first part: Map( this[type] == Point, $x : this[x] ) André -- Lisp is not dead. It’s just the URL that has changed: http://clojure.org/ ___ rules-users mailing list rules-users@lists.jboss.org https://lists.jboss.org/mailman/listinfo/rules-users
Re: [rules-users] Maps in Drools
Edson Tirelli schrieb: So, in principle having Maps support in the LHS is not a big challenge? No. Just requires developing a set of classes to work with maps. Being brief: * Implement: MapObjectType extends ObjectType * Implement: MapReadAccessor and MapWriteAccessor * Add support to them into the builders (i.e., the actual wiring of the new classes) * Test everything... I probably forgot something, since there's been quite some time since I worked on this. Would there not be an addition to the syntax needed, for the default rule language? For mvel it would not require a change I guess. Could you please explain this in a bit more detail? Lets call |A| the number of A facts in the working memory and |B| the number of B facts (i.e. cardinality). Lets call p(|A|,|B|) the number of partial matches a rule will create given the cardinality of A and B. Imagine your rule is: when A() B() then This rule creates a join between A() and B() and the number of join attempts will be: p(|A|,|B|) = |A| * |B| Now, if instead of representing A and B as different classes you use Maps with the type attribute as you was suggesting: $a : Map() $b : Map() eval( ... ) The number of partial matches will be: p(|A|,|B|) = (|A|+|B|)! / (|A|+|B|-2)! Ah yes okay, I see what you mean. Well, the current situation for me is that I have to do exactly that. As it is typical for Clojure apps to store data in Maps I need support for that. So, I want to allow rules like: (map-rule Rule name, type (when Customer ( $cust-id id ) DailyOrders (= 1 (get count $cust-id))) (then (println match))) This is semantically exactly the same as in your example, where Customer and DailyOrders were POJOs, while they are for me Maps. Draw the graph for these two functions and you will get the picture. If you have more than 2 patterns, situation gets worst and worst. If instead of eval() you use some alpha constraints, things go down to the same level as using different classes: $a : Map( this[type] == A ) $b : Map( this[type] == B ) The above will result in the same p(|A|,|B|) = |A| * |B| partial matches. Hmm, but the MVEL syntax can not magically eliminate the eval. Under the hood the map accesses will still be inside an eval. Marc confirmed that a few days ago. MVEL only hides this from the user. This is what I will also do. But under the hood it will become $a:Map() $b:Map() eval( $a.get(type) == Customer ) eval( $b.get(type) == DailyOrders ) And the only reason why I need to do it this way is because in its current version Drools does not support Maps as 1st class objects. If it would, then the eval would not be needed anymore. So, for Clojure users (and basically everyone else who stores data inside Maps, for whatever the reasons may be) it would be so good to have that support. You gave a perfect explanation of why this would be a great addition. The actual rule syntax is not important for me. My lib will hide it, as MVEL already does now. But my lib will compile the rules into the fast native rule language. It won't be interpreted or any slower than hand written rules. But currently I am forced to produce this cross product, as there is no direct support for Maps yet. 2009/8/19 André Thieme address.good.until.2009.dec...@justmail.de mailto:address.good.until.2009.dec...@justmail.de Edson Tirelli schrieb: I will skip the first half of your e-mail as I am not sure what were the reasons for your nit-picking. If my explanation was not helpful for the public it was intended to, you are welcome to explain yourself. Oh, I did not intend it to sound like nit-picking. I only meant that with a specialized syntax one can make rules operating on Maps looking basically identical to the ones operating on POJOs. Regarding the part that matters, i.e., adding the support to other fact types, Drools is prepared to support them in the LHS. Let me explain by example: when $c : Customer( name == bob ) then For the reasoning algorithm, does not matter if Customer is a POJO, an XML document, a Map with the type attribute set to Customer as you mentioned, or whatever you can think. We use a set of interfaces that allows us to completely abstract that and we even supported 2 different fact types as a proof of concept in the past. That sounds good! So, in principle having Maps support in the LHS is not a big challenge? As I understand it, code inside an eval can not be cached and needs to get executed every time and results in less performant code. The only reason we did not support multiple fact types yet for Drools is the RHS. Our reasoning is that does not make sense to abstract the LHS if you don't do the same
Re: [rules-users] Drools and Clojure
Mark Proctor schrieb: André Thieme wrote: Mark Proctor schrieb: Map( this['c'] == 206 ) That should work, we do support MVEL syntax for maps and arrays - we just don't suppor method calls, yet. Hello Mark. I just tested it and it indeed works for me. Although as I understand it, this will be compiled into an expression using eval (or it will even be only interpreted). So, if that is true it can't bring any performance advantage. yes, no performance benefit. As this form of use is a return value really, it should be possible to index (if we assume that nest objects do not change). Although it's a fair bit of work to do this, but interesting work - if anyone wants to help out :) It would be fantastic if there was the same optimization support for Maps available as it exists for POJOs. Edson gave a great explanation on how things slow down for cross products. I thought about one thing: Could there could maybe be a new attribute, immutable true or something like that, which would signal that the rule writer promises the engine that only immutable objects are used? That way my lib could implicitly always set that attribute. The syntax is nothing I worry about. In Clojure, which is a Lisp, I have macros and can remove any obstacles in the syntax I like. It is trivial to develop new domain specific languages for rules. So, my rule syntax for Clojure will look very lispy, and each user is free to change and extend it. I will also allow the RHS to be written in Clojure code, no Java needed. Btw if you are doing lispy stuff: http://blog.athico.com/2008/02/drools-clips.html http://blog.athico.com/2008/06/drools-clips-progress.html Ah yes, very interesting. In principle it is very similar what I do. With Clojure there is a full featured Lisp on the JVM. What I do is adding some functions and macros, easy to use, which will do all the Drools managing and hide it from the user. It will be simple to add all kinds of syntaxes as soon I am done. You can have the Jess syntax. Just write a macro and expand it into mine. It will result in compiled rules code. So, in some days (when I have time) the project above will be finished ;-) see Null-Safe Bean Navigation http://mvel.codehaus.org/MVEL+2.0+Property+Navigation Ah of course, I forgot the null. And MVEL already has the check built in. I think I will also implicitly add it. And another interesting thing I noticed: to both rules I added the line global String s; and in the LHS's of both rules I removed the c and put s at its place. Then the first thing I did after creating a session was to (.setGlobal session s c) The rule 1 accepted this change. I can use s instead of c. But there is still the limitation that I can only insert Maps into the session which do have the String c as key. Otherwise: NPE. The rule 2 (mvel) however does not accept s as a placeholder. I get: Exception executing predicate this[s] == 206 [Thrown class org.drools.RuntimeDroolsException] Any ideas how to get rid of the NPE? We'll have to look into this, probably a bug. Edson confirmed that this is an MVEL problem. In a different thread KDR may have discovered another one. André -- Lisp is not dead. It’s just the URL that has changed: http://clojure.org/ ___ rules-users mailing list rules-users@lists.jboss.org https://lists.jboss.org/mailman/listinfo/rules-users
Re: [rules-users] Maps in Drools
Mark Proctor schrieb: André Thieme wrote: Edson Tirelli schrieb: ooops... correct version: when Map( this[type] == Point, $x : this[x] ) Map( this[type] == Circle, this[x] == $x ) then end We default to MVEL, because it's simple and already contains everything we need for expression evaluation. However it would definitely be possible to implement this natively, effectively emulating MVEL - although you need to think what this actual buys? It doesn't buy indexing, as it's not MVEL that is the problem here. Ah okay, I didn't know that you are now defaulting to MVEL. Pretty much all the examples I found online and in the books don't use MVEL. You asked about what it buys: maybe nothing. I am very new to Drools and still need more experience. But now at least I *think* that MVEL rules run slower. I read on the official website that MVEL gets interpreted at runtime. Some days ago Greg did a little test: http://www.mail-archive.com/rules-users@lists.jboss.org/msg09839.html Right now it seems that Drools used from within Clojure would perform not too well in real world examples, because complex rules will have to look at 3-5 Maps. That would mean, as Edson explained, a cross product of all maps, which will reduce performance. If I don't use mvel I will manually say: $m1:Map() $m2:Map() $m3:Map() $m4:Map() eval ( $m1.get ...) eval ( $m2.get ...) and so on. In MVEL syntax it *looks* nicer, because I can make constraints directly by using the this keyword. But as MVEL is nothing but syntactic sugar over the real thing it will need to do the same when the program is running. On top of that, MVEL will be interpreted, thus resulting in even slower execution, although the rules will look more nicely. Now while I write this I just got a new thought: when the Drools engine is fed with the rules, it will also have to compile them, or interpret them at at runtime. I take a string s and do a s.getBytes(utf-8) and have this as an argument to ResourceFactory.newByteArrayResource. This is how I get rules into the Drools engine. It works. But now my new thought: shouldn't I be able to create new rules by purely writing Java code? If that were possible, then I would not compile my Clojure code into a string of either MVEL dialect or non-MVEL, but instead compile it to code which will do everything that would have happened if I had used a user readable string. Yeah, now that I think about it, then *this* is the right way how I should do it. That way I can do anything that Drools allows and not depend on any dialect. And my code would be compiled into byte code at runtime. No interpretation at all. Cool :) Now that I think about it: you could even trivially get a new dialect then with my library. dialect Lisp could be the attribute. That one would be as easy as mvel, but lispish (= more parens), but it would run at max speed, because it could be compiled directly into byte code. And it would be trivial to have a domain specific language with that. Could you please give me a start for a trivial example? rule points when Point( x == 0 ) then System.out.println(foobar); end where Point would be a public class Point { private int x; private int y; } plus two getters and a constructor. The rule points. If you would not have this as a file on disk or as a string in ram, but would want to add it with pure Java code, how would you then do it? André -- Lisp is not dead. It’s just the URL that has changed: http://clojure.org/ ___ rules-users mailing list rules-users@lists.jboss.org https://lists.jboss.org/mailman/listinfo/rules-users
Re: [rules-users] Maps in Drools
Edson Tirelli schrieb: On the general issue, is it received wisdom that it's better not to insert map objects direct, at least for now until map support is fully there - or is it 6 of one / half a dozen? Maps are data structures, not Domain entities. When we speak about equivalence in the mathematical sense then this statement is not true, as you say yourself: Using maps as domain entities is possible, Exactly. Instead of a class Point with the fields x and y we could have a Map with the three slots x, y and type, where the makePoint method would always return a Map whose type key always is set to Point. In Clojure this is an idiom. (Clojure is a language which compiles to Java Byte code and is fully compatible to the rest of Java.) but usually makes your rules unreadable. You are so deeply trained to think in Java that you forget the possibility of other syntaxes, in which it is perfectly readable. That is why it is bad to use any data structures or simple numbers, strings, dates as isolated facts... they don't have a well known business semantic in a given business model (not to mention how they get mixed with each other and cause cross products, etc). A rule like the following has no explicit meaning: when $str: String() $m: Map( this[$str] == 1 ) then But when you write something like: when Customer( $custId : id ) DailyOrders( count[$custId] == 1 ) then Things are clear just by looking at them, even if $custId is a String and count is a Map as in the original example. But now think about how this could be in Clojure. We would have a macro map-rule which tells Drools that we are using exclusively maps, and it may look like: (map-rule Rule name, type (when Customer ( $cust-id id ) DailyOrders (= 1 (get count $cust-id))) (then (println match))) The first argument is here type and tells the macro, that all maps do have a key type. It now dispatches to those. In the when part we first look at all Maps of type Customer. They may look like: {id 4, name Carlos, type Customer} {id 1, name Tina, type Customer} Then we look at all Maps of type DailyOrders. Those Maps may look like: {id 27, day java.util.Date Object, count {4 abc, 18 xyz}, type DailyOrders} get takes the key under which a DailyOrders instance has another Map as value, and accesses is with the key which is the second value here, the $cust-id. The when part looks exactly the same, only that I did not use the syntactic sugar for accessing maps. As soon I have enough time in the coming days I will write a Clojure lib which will make Drools easily usable from within Clojure. The good thing about that is that rules are not longer strings - they now become code. And because in Clojure Code = Data we will have the full power of Lisp available, and can write programs that write programs, such as rules. This easily opens rules to be automatically created via Genetic Programming, or, if the search space is not too huge, then all combinations of rules can be generated and tried out, to see which of those 18000 can solve my domain specific problem best. Currently the rule I showed above would become a Drools rule like: rule Rule name when m1:clojure.lang.APersistentMap() m2:clojure.lang.APersistentMap() eval( m1.get(type) == Customer ) eval( m2.get(type) == DailyOrders (m2.get(count)).get(m1.get(id)) == 1 ) then ((clojure.lang.IFn)globalVarWithClojureCode.get(17)).call()) end This would happen under the hood, and it would not be exposed to the users of my lib. I did not test this specific example code, and maybe I got something wrong, but that is the basic idea. Drools can work with this and does not have the readability problems of humans. In Clojure we can easily eliminate all readability isseus. I will offer some few macros for defining rules, and users can simply add others if they want. They won't have to care about what the underlying real Drools rules syntax looks like. They only expand their macros into my existing ones and have automatically optimized code, adopted to their readability needs in their specific domain. Now if the Drools Devs find a way how it would be possible to add the same optimization support for Maps as they currently already exist for POJOs, then rules acting on Maps could be very fast. I understand that the call to the method get() itself will be slower, because a hash value needs to be computed before the lookup can be made, but I think that lookup will not be the bottleneck. It would be fantastic if the Drools Devs could make it the bottleneck. In that case, that Maps would not need to be placed in eval anymore and become 1st class objects, exactly as POJOs, then get() would be the slowest part (which is now eval and full execution without caching). And Clojure Maps are also immutable. Drools can be
Re: [rules-users] Maps in Drools
Edson Tirelli schrieb: when Customer( $custId : id ) DailyOrders( count[$custId] == 1 ) then Btw, this brings me to a new syntax question for the default Drools rule syntax. Is this possible: when m:Map() eval( m.get(type) == Point, $x : m.get(x) ) ... Circle( x == $x ) then ... or, an alternative which may be clearer: when m:Map() eval( m.get(type) == Point ) $x : m.get(x) ... then ... So, what I would like to have is: 1) looking at all Maps which have the value Point under the key type 2) of all those Maps: store in a var what value they have under key x 3) reuse the var, here $x instead of having an expensive lookup over and over again. Is that possible? Or would I have to write my example above as ... c:Circle() eval(c.getX() == m.get(x)) André -- Lisp is not dead. It’s just the URL that has changed: http://clojure.org/ ___ rules-users mailing list rules-users@lists.jboss.org https://lists.jboss.org/mailman/listinfo/rules-users
Re: [rules-users] Maps in Drools
Edson Tirelli schrieb: I will skip the first half of your e-mail as I am not sure what were the reasons for your nit-picking. If my explanation was not helpful for the public it was intended to, you are welcome to explain yourself. Oh, I did not intend it to sound like nit-picking. I only meant that with a specialized syntax one can make rules operating on Maps looking basically identical to the ones operating on POJOs. Regarding the part that matters, i.e., adding the support to other fact types, Drools is prepared to support them in the LHS. Let me explain by example: when $c : Customer( name == bob ) then For the reasoning algorithm, does not matter if Customer is a POJO, an XML document, a Map with the type attribute set to Customer as you mentioned, or whatever you can think. We use a set of interfaces that allows us to completely abstract that and we even supported 2 different fact types as a proof of concept in the past. That sounds good! So, in principle having Maps support in the LHS is not a big challenge? As I understand it, code inside an eval can not be cached and needs to get executed every time and results in less performant code. The only reason we did not support multiple fact types yet for Drools is the RHS. Our reasoning is that does not make sense to abstract the LHS if you don't do the same for the RHS. So, for instance, using java dialect: when $c : Person( name == bob ) then $c.setAge( 30 ) ; end If we will support that rule, written as is, for POJOs, and we want to support Maps as facts, then our java parser needs to properly handle the consequence code as $c.put(age, 30). Same thing if Person was an XML document and so on. From my perspective the RHS is not important at all for my lib and for Clojure users who like to work with Drools. For me mostly one thing is interesting: getting Map lookups out of eval, so they can profit from exactly the same caching and optimizations that exist for POJOs. The RHS will be fully written in Clojure, and all challenges that occur in it would have to be solved by myself. If you Drools Devs could make it possible to give support for Maps in the LHS, then most issues for Clojure users could be solved. If you want to contribute to the project solving this problem, you are most welcome. Unfortunately I have not enough Java knowledege and not time. But I would like to contribute indirectly, by writing a lib for Clojure users which will make Drools easily accessible to them. It would also provide other users of Drools with an alternative syntax, which gets compiled into the default rule language. (Not into mvel, as that seems to be interpreted and runs a bit slower.) Regarding your rule rewrite, the way you propose is not feasible. Using multiple patterns of the same type without proper alpha constraints will lead to combinatorial explosion. Could you please explain this in a bit more detail? If Maps as 1st class rule objects, shouldn't my example then be exactly the same as the one that you gave? Your example was: when Customer( $custId : id ) DailyOrders( count[$custId] == 1 ) then ... My example was: rule Rule name when m1:clojure.lang.APersistentMap() m2:clojure.lang.APersistentMap() eval( m1.get(type) == Customer ) eval( m2.get(type) == DailyOrders (m2.get(count)).get(m1.get(id)) == 1 ) then ((clojure.lang.IFn)globalVarWithClojureCode.get(17)).call()) end And with direct Map support it could become something like: rule Rule name MapDefaultKey type Salience 2 when Customer( $custId : id ) DailyOrders( get(count, $custId) == 1 ) then ((clojure.lang.IFn)globalVarWithClojureCode.get(17)).call()) end Instead of the one could have syntactical sugar which may look unfamiliar: count[$custId] If the key is not a string but a float one would even have 88.5[$custId] instead get(88, $custId). This is just a matter of taste, and users could have their DSLs anyway. André -- Lisp is not dead. It’s just the URL that has changed: http://clojure.org/ ___ rules-users mailing list rules-users@lists.jboss.org https://lists.jboss.org/mailman/listinfo/rules-users
Re: [rules-users] Drools and Clojure
Greg Barton schrieb: 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. Thanks for confirming that. For now I know that I am doing it right by using eval and that this means that rules for typical Clojure objects will not benefit from some of the optimizations Drools usually can apply. Maybe it will change in the future. 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. I think that if Maps become 1st class objects there could be a different Syntax, without using eval. 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.) At this point it is mostly interesting for me to get Drools working with Clojure together and concentrate on correctness. The goal is that users of my lib can use do all typical things people do with Drools without writing Java code. Everything, including the rules, would be written in Clojure. But of course it would be very interesting if the devs could indeed have Clojure in mind. I don't know how reusable the existing code is, for using Maps without the need for eval and with having the respective performance advantages. I understand that the looup of the value for a given key can not be optimized away. On my hardware get() is limited to only 1000 calls per msec and core. Reading a field from a POJO is faster. If I understand it correctly then the problem with eval is that it needs to be executed each time, and no clever caching can be done. So, eliminating that by having Maps being 1st class is what sounds interesting. When I refer to Maps and Clojure, then I talk about Maps having only very few key/value pairs, just like POJOs, and being immutable. This immutability may even be very helpful for optimization. It is guaranteed that a given object will never change. I hope the devs will find it okay if I make some concrete suggestions in the coming weeks. Sunny greetings, André -- Lisp is not dead. It’s just the URL that has changed: http://clojure.org/ ___ rules-users mailing list rules-users@lists.jboss.org https://lists.jboss.org/mailman/listinfo/rules-users
Re: [rules-users] Drools and Clojure
Mark Proctor schrieb: Map( this['c'] == 206 ) That should work, we do support MVEL syntax for maps and arrays - we just don't suppor method calls, yet. Hello Mark. I just tested it and it indeed works for me. Although as I understand it, this will be compiled into an expression using eval (or it will even be only interpreted). So, if that is true it can't bring any performance advantage. The syntax is nothing I worry about. In Clojure, which is a Lisp, I have macros and can remove any obstacles in the syntax I like. It is trivial to develop new domain specific languages for rules. So, my rule syntax for Clojure will look very lispy, and each user is free to change and extend it. I will also allow the RHS to be written in Clojure code, no Java needed. But back to your example: I noticed something very interesting: when I use the MVEL dialect for the Map lookup, then I get no NPE anymore when I check in a Map which does *not* have the key I test for. So, now I have two versions of my rule: package droolsandclojure; import java.util.Map; rule Clojure test 1 when m:Map() eval((Integer)m.get(c) == 206) then System.out.println(Match: + m); end and package droolsandclojure; import java.util.Map; rule Clojure MVEL test 2 dialect mvel when m:Map( this[c] == 206 ) then System.out.println(Match: + m); end Only one of these two rules is used, not both at the same time. When I use rule 1 then I can not insert Maps into my session which do not have a key c. If I try it and run my code I get a NPE. When I use rule 2, the MVEL version, this is different. Now I can insert any Maps and will not get an exception. The rule will simply just not execute the RHS. How can rule 1 be changed so that it will not put a constraint on the objects which are allowed to go into the session without throwing a NPE? And another interesting thing I noticed: to both rules I added the line global String s; and in the LHS's of both rules I removed the c and put s at its place. Then the first thing I did after creating a session was to (.setGlobal session s c) The rule 1 accepted this change. I can use s instead of c. But there is still the limitation that I can only insert Maps into the session which do have the String c as key. Otherwise: NPE. The rule 2 (mvel) however does not accept s as a placeholder. I get: Exception executing predicate this[s] == 206 [Thrown class org.drools.RuntimeDroolsException] Any ideas how to get rid of the NPE? -- Lisp is not dead. It’s just the URL that has changed: http://clojure.org/ ___ rules-users mailing list rules-users@lists.jboss.org https://lists.jboss.org/mailman/listinfo/rules-users
Re: [rules-users] Drools and Clojure
With what have you been wrong Greg? I understood Marks reply in such a way that the mvel dialect allows to use Maps without eval. But this is just syntactic sugar. When compiled or interpreted (I don't know which of those Drools will do with mvel rules), this will get replaced with an eval under the hood. So, that means that still the optimizations can't be applied. Maybe the mvel rule is only interpreted, which would make it even slower. For me it does not matter much which syntax will be my target. My Clojure lib will allow to write down rules in s-expression syntax, and it will compile them (compilation is also available at runtime) into any syntax Drools accepts. Currently my target is the default syntax - that means I will produce strings that access Maps with eval. IF the mvel syntax will result in more performant code because it makes Drools handle it better without using eval under the hood, then sure, I can also compile into mvel syntax. Although with that I still have the little problem to have globals in place of literals for Map lookups (see my other mail). Greg Barton schrieb: I was wrong. :). See Mark's later email. GreG On Aug 13, 2009, at 17:25, André Thieme address.good.until.2009.dec...@justmail.de wrote: Greg Barton schrieb: 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. Thanks for confirming that. For now I know that I am doing it right by using eval and that this means that rules for typical Clojure objects will not benefit from some of the optimizations Drools usually can apply. Maybe it will change in the future. 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. I think that if Maps become 1st class objects there could be a different Syntax, without using eval. 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.) At this point it is mostly interesting for me to get Drools working with Clojure together and concentrate on correctness. The goal is that users of my lib can use do all typical things people do with Drools without writing Java code. Everything, including the rules, would be written in Clojure. But of course it would be very interesting if the devs could indeed have Clojure in mind. I don't know how reusable the existing code is, for using Maps without the need for eval and with having the respective performance advantages. I understand that the looup of the value for a given key can not be optimized away. On my hardware get() is limited to only 1000 calls per msec and core. Reading a field from a POJO is faster. If I understand it correctly then the problem with eval is that it needs to be executed each time, and no clever caching can be done. So, eliminating that by having Maps being 1st class is what sounds interesting. When I refer to Maps and Clojure, then I talk about Maps having only very few key/value pairs, just like POJOs, and being immutable. This immutability may even be very helpful for optimization. It is guaranteed that a given object will never change. I hope the devs will find it okay if I make some concrete suggestions in the coming weeks. Sunny greetings, André -- Lisp is not dead. It’s just the URL that has changed: http://clojure.org/ ___ rules-users mailing list rules-users@lists.jboss.org https://lists.jboss.org/mailman/listinfo/rules-users
[rules-users] Drools and Clojure (maps as facts)
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. HashMapString, Object hm = new HashMapString, 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@lists.jboss.org https://lists.jboss.org/mailman/listinfo/rules-users