-
Notifications
You must be signed in to change notification settings - Fork 110
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add no-loop option for rules #23
Comments
Clara uses logical inserts in rules, so if the conditions that caused a rule to fire become false, the inserted fact is automatically retracted. This is the desired behavior in most use cases (or at least the ones I had in mind when designing this). For instance, if we insert a Temperature of 20 degrees and a rule declares that as Cold, we want to retract the Cold fact if the inserted Temperature is retracted. In this case, since we're retracting the Glass fact that triggered the rule, it also retracts facts the rule inserted itself. Of course, that doesn't help this scenario. I know other rules engines support logical and unconditional inserts, so we could address this by doing the same. It would be straightforward to add an insert-uncoditional!, that would not retract facts it inserts in the event the instance of the rule that fired becomes false. |
insert-unconditional! would definitely be helpful in a few of the use-cases that I have. |
Added an insert-unconditional! function as described here. I haven't verified your specific use case now works, but this functionality made sense and is tested in a simpler unit test. |
So one other feature that would help out would be the 'no-loop' concept from drools, where you can specify whether the rule will refire if the conditions are met after retracting/inserting a new fact in the rhs. The above example would look something like this: I get some strange behavior when I have circular types of rules: (defrule-no-loop optimist
[?g <- Glass (= ?full full) (= ?capacity capacity)]
[Faucet (= ?flow flow)]
[:test (not= ?full ?capacity)]
=>
(retract! (->Glass ?full ?capacity))
(insert! (->Glass (+ ?full ?flow) capacity))
(println ?g))
|
If you wouldn't mind, I wonder if you could describe your use case in a bit more detail? Adding no-loop is doable but I'd like to understand the problem space a bit further in case there may be other (possibly cleaner) changes we can make to support it. |
Example for no-loop from the drools book would be: (defrule add-interest
[?a <- Account (= ?balance balance) (= ?interest)]
=>
(retract! ?a)
(insert! (compound ?balance ?interest)) LHS and RHS share a fact that is altered non-recursively |
Fair enough. We can probably do this with a relatively simple change:
I'll plan on jumping into this as time permits…it might be a week or so due to other demands. Of course, I'd be happy to work with a patch if anyone was interested in taking a stab at it. |
The previous commit adds :no-loop support to master. The example rule below shows this in action, retracting a temperature and adding a new one one degree cooler, and does not re-trigger the rule since it is :no-loop. (defrule reduce-temp-one-degree
"Example rule to reduce temperature."
{:no-loop true}
[?t <- Temperature (== ?v temperature)]
=>
(do
(retract! ?t)
(insert-unconditional! (->Temperature (- ?v 1) "MCI")))) I also deployed a 0.3.0-SNAPSHOT build of this logic to the Sonatype snapshot repository. |
Marking this as close due to the previous commit and comment. We can reopen if you disagree. |
I noticed that you used Is there any particular reason for this? I'm just curious if logical insert would behave as expected in a rule like: (defrule inserts-one-temp
{:no-loop true}
(not [Temperature (> temperature 0)])
=>
(insert! (->Temperature 100 "MCI"))) I have tested this and it seems to work like I'd expect. I have came across a use-case like this a few times and I'm making sure that this would be reliable. |
No special reason, just logic specific to the use case I was showing in the example. I did that because I only wanted one temperature setting in the working memory (hence the retract!), and I used insert-unconditional! since it inserts a new fact that doesn't depend on the rule doing the insert to be continue to be true. |
That makes sense. Thanks for the explanation. I was glad to see that the logical insert was not retracted in my example above. In Drools this would have prevented a loop, but it would retract the logical insertion, which I do not think is very intuitive. Clara++ |
I updated the issue description to reflect the change made and assigned it to the 0.3.0 milestone in preparation for release. |
I get some strange behavior when I have circular types of rules:
I was hoping this would work something like:
I was wonder what your thoughts are on handling a rule like this
The text was updated successfully, but these errors were encountered: