KnowledgeHelper drools and functions

612 views
Skip to first unread message

David "Borris" Fell

unread,
Oct 1, 2014, 5:21:13 PM10/1/14
to drools-de...@googlegroups.com
I like being able to declare functions within DRL files. It permits common code to be written once, rather than being duplicated - which is a good thing (TM).

But it feels cumbersome to have to write "KnowledgeHelper drools" as an argument for so many of them (in order to access insert/delete especially). And, because of the magic meaning, modify isn't available in functions in the same way as consequences.

It would be really cool if functions could have the same level of automatic access to insert, delete, modify and drools.method that a rule consequence gets. I've no idea how far the implications of this go for the implementation, but from an authoring viewpoint, it would save a lot of repetitive character sequences and feels "more rounded".

Borris


Davide Sottara

unread,
Oct 1, 2014, 10:45:45 PM10/1/14
to drools-de...@googlegroups.com
This time I will answer from a purely personal perspective :)

It could be done technically, but I suspect it would further the use of bad practices.. I usually complain that a rule's RHS is already too liberal :)

A function (as the name states :)) can be appropriately used to compute data, but then the result should be used by WM actions in the RHS.
So, I'd do "then insert( foo() )" or "then modify ( x ) { setField( bar( x ) ); }"
rather than "then foo( drools )" or "then bar( x, drools )".

Moreover, I'm afraid that it would actually encourage the use of procedures, rather than functions, effectively defeating the purpose
of the rule engine. Imagine for example:

when $o : Object() then do( $o ) end
function void do( Object x ) { if ( x instanceOf A && ((A) x).getValue > 10 ) { delete( x ); } else ... }

Maybe you have a more specific class of problems where functions are a proper requirement and have a well-defined application?


One example could be the case when multiple rules need, in practice, to execute the same conclusion:
when A( $x : val ) then foo( $x ) end
when B( $x: val ) then foo( $x ) end

In that case, however, one can use "ORs" to factor the conclusion :
when A( $x : val ) or B( $x : val ) then insert( foo( $x ) ) end

or even use explicit chaining (maybe even reifying some proper domain entity):

when A( $x : val ) then insert( new Foo( $x ) ); end
when B( $x : val ) then insert( new Foo( $x ) ); end
when $y : Foo( $x ) then delete( $y ); insert( foo( $x ) ); end

Open for discussion
Davide
--
You received this message because you are subscribed to the Google Groups "Drools Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to drools-developm...@googlegroups.com.
To post to this group, send email to drools-de...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/drools-development/0805e58e-b309-4268-9c98-4f91606c2466%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

David "Borris" Fell

unread,
Oct 2, 2014, 8:57:09 AM10/2/14
to drools-de...@googlegroups.com
I can see the risk of bad practises - your example with a conditional in a function is indeed something that should make people nervous.

I use functions for a few patterns:

1) Debug assistance. For example

function void retractTrigger( KnowledgeHelper drools, Trigger trigger )
{
drools.retract( trigger );
Debug.log( "Retracted %s\n", trigger );
}

in this sort of situation, a function gives me two things. It removes the need to keep writing the same pattern again and again. And it gives me a single enable/disable point for the debug message, making it a small action to reliably turn all debug of this type on/off without hunting over multiple files. (BTW personally I much prefer retract over delete, although I do note the documentation says retract is now deprecated in favour of delete. So many things have methods called delete, whereas retract (to me) stands out as specifically being drools).

2) Simple effort reduction - basic macro behaviour

function void throwRuntimeException(KnowledgeHelper drools, Object obj)
{
throw new RuntimeException( "FAILURE: " + drools.getRule().getName() + ": " + obj);
}

Having a single function means my convention on presentation is contained in only one place.

3) Similar to your "multiple rules need, in practice, to execute the same conclusion"

function Consumer newConsumer( KnowledgeHelper drools, Technique t, boolean inside, String name, ZOM zomness, Class clazz )
Consumer c = new Consumer( t, inside, name );
drools.insert( c );
drools.insert( new ZOMConstraint( c, zomness ) );
drools.insert( new ClassConstraint( c, clazz ) );
return c;
}

This shape of usage is where multiple facts need asserting into the WS as a result of the one action. Writing that sort of sequence out each time is error prone, time consuming and tedious if, for example, a third supporting fact needs inserting.

In some situations your approach of inserting some form of trigger fact is an appropriate structure, I agree. And indeed I have similar approaches in places. Sometimes the OR approach is reasonable. But sometimes the different conditions are many and spread across files and pulling them together into one rule wouldn't feel a good shape of outcome.

4) Usage of modify

The reason for calling out modify is when a state change is needed. For example

function void actionCore( KnowledgeHelper drools, Technique action, TechniqueState ts )
{
trace( drools );
Provider primary_i = newProvider( drools, action, true,  "primary_i", ZOM.MUST, FoodItem.class );
Consumer yield_i =  newConsumer( drools, action, true,  "yield_i",  ZOM.MUST, CompositeIngredient.class );
drools.insert( new ProtoBoundary3p( action, "primary", primary_i ) );
drools.insert( new ProtoBoundary3c( yield_i, action, "yield" ) );
drools.insert( new Joiner( primary_i, yield_i ) );
}

rule "represent action clean"
when
$ts: TechniqueState( $action, State.ROMEO; $action instanceof Action, $action#Action.name == "clean" )
TechniqueConstraintsSatisfied( $action; )
then
actionCore( drools, $action, $ts );
modify( $ts ) { setState( State.PAPA ) }
end

Ideally for this shape I would want the state modifier in the function as well. 


In summary, there are a range of code shapes where functions offer the hope of writing something once rather than many times, and offer simplicity if the (otherwise repetitive sequence) needs to be changed or tweaked in the future.

I guess it's another example of a classic dilemma - how to provide flexibility whilst discouraging "bad things".

Borris

Davide Sottara

unread,
Oct 2, 2014, 12:45:22 PM10/2/14
to drools-de...@googlegroups.com
Looking at your use cases
#1 could probably be implemented better with a WM listener

#2 is something where I would pass the argument explicitly since it's part of the logic

#3 and #4 are more interesting. I think that what you are advocating for is not a "generic function where the KH is injected",
but the ability to extrapolate a rule's consequence block from the rule itself, give it a name and reference it from the RHS,
possibly from multiple rules, rather than inlining it directly. That is,

rule "R" when ... then Foo end
rule "S" when .. then Foo end

consequence Foo  { /* */ }


Notice that rule consequences are already implemented as functions, where the args are inferred from the declarations
in the LHS, plus some injections like the KH, and we give them internal, unique names, so this would probably not be a huge
change. The discussion would then be about making arguments explicit or not - that is, should Foo have a signature, or would
we keep inferring it (in which case, R and S should be compatible)?

This could be a more controlled way of doing things and might be further considered. @Mark, @Edson, @Mario, any thought?
Davide

--
You received this message because you are subscribed to the Google Groups "Drools Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to drools-developm...@googlegroups.com.
To post to this group, send email to drools-de...@googlegroups.com.

David "Borris" Fell

unread,
Oct 2, 2014, 2:20:25 PM10/2/14
to drools-de...@googlegroups.com
That's an interesting thought! My initial gut reaction is that you want to try and parametrise if possible as it gives more flexibility - and it feels almost inevitable that the first real solid usage could work with parameters and not have enough expressivity without. I'll give it more thought though and come back later as well.

Borris

Matteo Mortari

unread,
Oct 8, 2014, 8:32:53 AM10/8/14
to drools-de...@googlegroups.com
Hi,
just to mention, I believe I have almost exactly the same use-case/requirement, similar case to #3 and #4 and indeed where the ability to have "function where the KH is injected" would really be helpful, to access for instance global variables state and session clock.

My Example (highly approximated from a real case scenario):

rule "R" when ... then createAlert("The R problem has occured") end

rule
"S" when ... then createAlert("The S problem has occured") end

function void createAlert(String alertName) {
   
Alert a = new Alert();
    a
.setName(alertName);
    a
.setOccurenceTs(new java.util.Date(kcontext.getKnowledgeRuntime().getSessionClock().getCurrentTime()));
}

At present the only way I can create something similar, is that I got to have KH in the signature of the function and pass with kcontext in the RHS of the rules when calling the function.

Ciao,
MM
Reply all
Reply to author
Forward
0 new messages