Re: [rules-users] Maps in Drools
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. In other words: Map( this[type] == Point, $x : this[x], size == 5 ) 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. 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 []s Edson 2009/8/20 André Thieme address.good.until.2009.dec...@justmail.de 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 -- Edson Tirelli JBoss Drools Core Development JBoss by Red Hat @ www.jboss.com ___ rules-users mailing list rules-users@lists.jboss.org https://lists.jboss.org/mailman/listinfo/rules-users
Re: [rules-users] Maps in Drools
2009/8/20 André Thieme 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. 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. 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. There are 2 types of eval(). Inline eval() and top level eval(). 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. 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 ) ) 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 combinaions possible between all maps as above, it will first find all Customer maps and all DailyOrders maps and only after that will make a join between them. So you get Customers * DailyOrders partial matches. The above evals are semantically equivalent as: $a:Map( this[ type ] == Customer ) $b:Map( this[ type ] == DailyOrders ) But currently I am forced to produce this cross product, as there is no direct support for Maps yet. I hope that by the above you see that the problem of the cross products is not a problem with Maps support, but rather a question of how to write better rules. The same way you can write 2 completely different SQL queries that return the same result but one is fast and the other is completely heavy and slow, you can also write good rules and really bad rules. []s Edson ___ 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 for
Re: [rules-users] Maps in Drools
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. Mark 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é ___ 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
Thanks again Edson. I'd just used a String object to try a simple test but of course your example makes a lot more sense. And thanks also for clarifying that there's full syntax support in the latest mvel jar version. I know this is a Drools rather Java list but as I'm new to both, may I ask further how to install that new jar version you mentioned into my current Eclipse Drools project, or indeed how to get new Drools projects in Eclipse to use it please, instead of the old version? I've tried copying the jar into the Drools runtime folder but it doesn't work? Cheers Edson Tirelli-3 wrote: 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. Using maps as domain entities is possible, but usually makes your rules unreadable. 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. Regarding the bug, it was a regression that was fixed. All the syntax support we intended to have for them is in Drools. Not sure what you mean by support is fully there. Hope it helps. []s Edson -- View this message in context: http://www.nabble.com/Maps-in-Drools-tp25031348p25045607.html Sent from the drools - user mailing list archive at Nabble.com. ___ 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
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. 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. 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. If you want to contribute to the project solving this problem, you are most welcome. 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. []s Edson 2009/8/19 André Thieme address.good.until.2009.dec...@justmail.de 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
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] Maps in Drools
ooops... correct version: when Map( this[type] == Point, $x : this[x] ) Map( this[type] == Circle, this[x] == $x ) then end 2009/8/19 Edson Tirelli tire...@post.com when Map( this[type] == Point, $x : this[x] ) Map( this[type] == Circle, x == $x ) then end 2009/8/19 André Thieme address.good.until.2009.dec...@justmail.de 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 -- Edson Tirelli JBoss Drools Core Development JBoss by Red Hat @ www.jboss.com -- Edson Tirelli JBoss Drools Core Development JBoss by Red Hat @ www.jboss.com ___ rules-users mailing list rules-users@lists.jboss.org https://lists.jboss.org/mailman/listinfo/rules-users
Re: [rules-users] Maps in Drools
Yes, that was a bug that was fixed in newer versions of MVEL. Just update your MVEL jar to 2.0.12. []s Edson 2009/8/18 André Thieme address.good.until.2009.dec...@justmail.de KDR schrieb: Hi, I'm relatively new to both Java and Drools. I'm trying to figure out how to use maps in Drools. I've looked at the thread http://www.mail-archive.com/rules-users@lists.jboss.org/msg09802.html From what I've read generally it seems best to insert objects directly rather than use nested accessors. So I've been experimenting with trying to insert a map and then checking stuff in it. I set up a simple test map of String to Integer, with just a as key and 1 as value, and b with 2. MapString, Integer map = new HashMapString, Integer(); map.put(a, 1); map.put(b, 2); String a = a; I then inserted the map and also inserted the String a of value a. Here's the test rule, with various things I tried commented out: rule testing maps dialect mvel when $str: String() // $m: Map( this[$str] == 1 ) # error // $m: Map( this.$str == 1 ) # error // $m: Map( this[$str] == 1 ) # compiles but rule won't fire $m: Map( this[a] == 1 ) # this works however! then System.out.println($m[$str]); #also works with String and Map objects no conditions end It obviously doesn't like it when I try to use the String object as the key for the map. But it works when I use a String literal as the key. What am I doing wrong? Does anyone have any suggestions please, or shall I give up and either use eval as mentioned in http://www.mail-archive.com/rules-users@lists.jboss.org/msg09716.html or use the map as a field of another object which I insert instead of the map (in fact that was my original plan!)? In exactly this thread Marc answered to that problem. http://www.mail-archive.com/rules-users@lists.jboss.org/msg09837.html His first idea is that this is a bug in the mvel dialect. I tried exactly that. I had a global var and put the string a into it. Then I wanted to check if Map( this[myVar] == 1 ) but this didn't work. I'd also need to test for null i.e. whether a key/value pair exists for a given String as the key. This seems to be only true when you go the route that I go, namely using the default rule syntax, i.e., eval. 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 -- Edson Tirelli JBoss Drools Core Development JBoss by Red Hat @ www.jboss.com ___ rules-users mailing list rules-users@lists.jboss.org https://lists.jboss.org/mailman/listinfo/rules-users
Re: [rules-users] Maps in Drools
Apologies, I should have given the error message I get with $m: Map( this[$str] == 1 ) and also with $m: Map( this.$str == 1 ) - org.drools.RuntimeDroolsException: Exception executing predicate this[$str] == 1 and lots more lines followed by - Caused by: [Error: unable to resolve method: java.util.HashMap.$str() [arglength=0]] KDR wrote: Hi, I'm relatively new to both Java and Drools. I'm trying to figure out how to use maps in Drools. I've looked at the thread http://www.mail-archive.com/rules-users@lists.jboss.org/msg09802.html From what I've read generally it seems best to insert objects directly rather than use nested accessors. So I've been experimenting with trying to insert a map and then checking stuff in it. I set up a simple test map of String to Integer, with just a as key and 1 as value, and b with 2. MapString, Integer map = new HashMapString, Integer(); map.put(a, 1); map.put(b, 2); String a = a; I then inserted the map and also inserted the String a of value a. Here's the test rule, with various things I tried commented out: rule testing maps dialect mvel when $str: String() // $m: Map( this[$str] == 1 ) # error // $m: Map( this.$str == 1 ) # error // $m: Map( this[$str] == 1 ) # compiles but rule won't fire $m: Map( this[a] == 1 ) # this works however! then System.out.println($m[$str]); #also works with String and Map objects no conditions end It obviously doesn't like it when I try to use the String object as the key for the map. But it works when I use a String literal as the key. What am I doing wrong? Does anyone have any suggestions please, or shall I give up and either use eval as mentioned in http://www.mail-archive.com/rules-users@lists.jboss.org/msg09716.html or use the map as a field of another object which I insert instead of the map (in fact that was my original plan!)? I'd also need to test for null i.e. whether a key/value pair exists for a given String as the key. Any thoughts would be very much appreciated. Many thanks in advance. -- View this message in context: http://www.nabble.com/Maps-in-Drools-tp25031348p25031731.html Sent from the drools - user mailing list archive at Nabble.com. ___ rules-users mailing list rules-users@lists.jboss.org https://lists.jboss.org/mailman/listinfo/rules-users
Re: [rules-users] Maps in Drools
Many thanks for your replies André and Edson. For some reason they hadn't shown up on my computer before I posted my follow up info. I'll download the updated jar and cross my fingers! 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? André on your point below: André Thieme-4 wrote: KDR schrieb: I'd also need to test for null i.e. whether a key/value pair exists for a given String as the key. This seems to be only true when you go the route that I go, namely using the default rule syntax, i.e., eval. - I don't think I quite follow? If I need to check whether the key/value pair exists for a particular String key i.e. whether the get(key) returns null, is the only way to do that currently by using the eval? Cheers and thanks again for the help. -- View this message in context: http://www.nabble.com/Maps-in-Drools-tp25031348p25034293.html Sent from the drools - user mailing list archive at Nabble.com. ___ rules-users mailing list rules-users@lists.jboss.org https://lists.jboss.org/mailman/listinfo/rules-users