Principles of Software Flow



Playing the Fool With Drools

The Fool is… the childlike ability to tune into the inner workings of the world.

Learning through play

I want to get to know Drools properly.  The core fascinates me but it always seems to be obscured from view.  I want to get past the layers of sophistication and tune into the inner workers of the rules engine.

So I’m going to take a different approach to learning.  I’m going to fool around with Drools and learn through play.

I’m going to perform playful experiments with five key features of Drools that interest me.

The Drools Rule Engine

Over the next few weeks I’m going to post some articles playing with the following features.  I’ll try to take the feature and use them in a way that is different from the usual examples.  I doubt I’ll create anything useful.  I may learn a few useful things.  Only one thing is certain: I’m going to enjoy myself.

1.Declaring Classes

It’s possible to declare new Classes within the DRL file.

The reference guide gives the following example:

declare Person
    name : String
    dateOfBirth : java.util.Date
    address : Address
end

Which is the equivalent to this Java code:

public class Person implements Serializable {

    private String name;

    private java.util.Date dateOfBirth;

    private Address address;

    // empty constructor

    public Person() {...}

    // constructor with all fields 

    public Person( String name, Date dateOfBirth, Address address ) {...}

    // if keys are defined, constructor with keys

    public Person( ...keys... ) {...}

    // getters and setters

    // equals/hashCode

    // toString

 

As you can see, the Drools syntax is far more concise than the full blown Java.  It also saves me having to worry about implementing hashCode and equals.

2. Traits

Traits allow you to implement a form of multiple inheritance.

The Drools examples include a demonstration of traits that extends the Person class.


declare Parent
 @format ( trait )
 children : List
end 

declare Father
end

declare Mother
end

rule xxx when
 $person : Person()
 exists ( Person() from $person.fields[ "children" ] )
then
 don( $person, Parent.class, true );
end 

rule xxx when
 $parent : Parent( gender == "male" )
then
 don( $per, Father.class, true );
end 

rule xxx when
 $parent : Parent( gender == "female" )
then
 don( $per, Mother.class, true );
end 

rule xxx when
 $father : Father()
 Today( this == Sunday )
then
 System.out.println( $father + ": It's your day off" );
end

I’d be interested to see what I could do with that particular feature.  It appears to allow a form of specialisation by constraint.

3. Streams

Fusion introduces support for Streams.  This allows streams of events to be processed, like in this example:

rule "authorize withdraw"
when
    WithdrawRequest( $ai : accountId, $am : amount ) from entry-point "ATM Stream"
    CheckingAccount( accountId == $ai, balance > $am )
then
    // authorize withdraw
end

I wonder if I can use Streams to implement Flow Based Programming using Drools?

4. Queries

Queries seem to be the easiest way to get data out of working memory.  They are clear and easy to read, like this example:

query "people over the age of 30" 
    person : Person( age > 30 )
end

Behind this simplicity lurks something far more complex. Consider this recursive example:

declare Location
    thing : String 
    location : String 
end
 
query isContainedIn( String x, String y ) 
    Location(x, y;)
    or 
    ( Location(z, y;) and ?isContainedIn(x, z;) )

What is even more interesting is the explanation given for the ‘?’ prefix:

The ‘?’ symbol means the query is pull only, once the results are returned you will not receive further results as the underlying data changes.

Does this mean that you can have push queries that do continue to provide results as the underlying data changes?

5. Backward Chaining

What on earth is Backward Chaining?   The documentation explains:

Drools supports unification for derivation queries, in short this means that arguments are optional. It is possible to call queries from Java leaving arguments unspecified using the static field org.drools.core.runtime.rule.Variable.v – note you must use ‘v’ and not an alternative instance of Variable. These are referred to as ‘out’ arguments. Note that the query itself does not declare at compile time whether an argument is in or an out, this can be defined purely at runtime on each use.

No.  I’m none the wiser.

Perhaps the example will makes things clearer:

package org.drools.examples.backwardchaining;

import org.drools.examples.backwardchaining.Location;

dialect "mvel"

query isContainedIn( String x, String y )
 Location( x, y; )
 or
 ( Location( z, y; ) and isContainedIn( x, z; ) )
end

rule "go" salience 10
when
 $s : String( )
then
 System.out.println( $s );
end


rule "go1"
when
 String( this == "go1" )
 isContainedIn("Office", "House"; )
then
 System.out.println( "office is in the house" );
end

rule "go2"
when
 String( this == "go2" )
 isContainedIn("Draw", "House"; )
then
 System.out.println( "Draw in the House" );
end

rule "go3"
when
 String( this == "go3" )
 isContainedIn("Key", "Office"; )
then
 System.out.println( "Key in the Office" );
end

rule "go4"
when
 String( this == "go4" )
 isContainedIn(thing, "Office"; )
then
 System.out.println( "thing " + thing + " is in the Office" );
end

rule "go5"
when
 String( this == "go5" )
 isContainedIn(thing, location; )
then
 System.out.println( "thing " + thing + " is in " + location );
end

I can’t see anything special here. Perhaps there’s something in the java:

package org.drools.examples.backwardchaining;

import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;

import java.text.SimpleDateFormat;

public class HouseOfDoomMain {

 public static void main(String[] args) throws Exception {
 KieContainer kc = KieServices.Factory.get().getKieClasspathContainer();
 KieSession ksession = kc.newKieSession( "HouseOfDoomKS");

 ksession.insert( new Location("Office", "House") );
 ksession.insert( new Location("Kitchen", "House") );
 ksession.insert( new Location("Knife", "Kitchen") );
 ksession.insert( new Location("Cheese", "Kitchen") );
 ksession.insert( new Location("Desk", "Office") );
 ksession.insert( new Location("Chair", "Office") );
 ksession.insert( new Location("Computer", "Desk") );
 ksession.insert( new Location("Draw", "Desk") );

 ksession.insert( "go1" );
 ksession.fireAllRules();
 System.out.println("---");

 ksession.insert( "go2" );
 ksession.fireAllRules();
 System.out.println("---");

 ksession.insert( "go3" );
 ksession.fireAllRules();
 System.out.println("---");

 ksession.insert( new Location("Key", "Draw") );
 ksession.fireAllRules();
 System.out.println("---");

 ksession.insert( "go4" );
 ksession.fireAllRules();
 System.out.println("---");

 ksession.insert( "go5" );
 ksession.fireAllRules();
 }
}

Is it just me? What is going on? What is this Backward chaining actually doing?

I’m going to have some fun finding out.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: