[rules-users] How to track constraint truth [was: Non short circuit ANDing]

Wolfgang Laun wolfgang.laun at gmail.com
Sat Feb 2 05:26:21 EST 2013


To summarize the discussion of the major part of the cited thread:
   * There are situations when it is of interest to learn which of the
several constraints of a rule are true and which block it from firing.
   * It is known that manually writing a set cooperating rules can
solve this problem (e.g., Rule Design Pattern "Learning the Reason for
Failure")
   * Some automatism supporting the creation of such a set would help

Considering such a support, one wonders which level of complexity in
the primary rule it must be able to handle. The task in itself
procures a limitation, e.g., when one pattern must match so that a
binding is available which is used in a successive pattern. (More
formally: the dependency graph of bindings restricts the investigation
of the truth of LHS sub-terms.) Furthermore, any solution using
reverse-engineering from the internal representation of a LHS isn't
simple, and the API for accessing a compiled rule isn't "stable".

But I think that simple scenarios can be handled by a simple rule
authoring tool. I'll explain this by using an example: a rule
investigating offered cars according to a buyer's wishes:
class Car {
   String colour;
   int price;
   int horsepower;
   boolean abs;
}

rule "car"
when
   $car: Car( colour == "red", price < 1000, horsepower >= 150, abs == true )
then ... end

To permit the individual investigation of constraints, this might be
rewritten as:

rule "car"
when
   $car: Car()
then end

rule "car colour == \"red\"" extends "car"
when
  Car( this == car, colour == "red" )
then // Gosh, a red one!
end

rule "car price < 1000" extends "car"
when
  Car( this == car, price < 1000 )
then // Hey, it's cheap enough!
end

And so on. - For a fully automatic process, a "Collector" fact has to
be maintained for each fact (or Cartesian product of facts) matching
the initial rule (i.e., "car"). Into this Collector, positive results
from the extending rules are injected, so that, ultimately, it can be
examined for full information. Here's a couple of examples of rules
doing just that, with the rule id and failure count as positional
properties:

rule "success"
when
    $c: Collector( "car", 0; )
then
    System.out.println( "Found the car I want: " + $c.get( "car" ) );
end

rule "almost"
when
    $c: Collector( "car", 1; )
then
    System.out.println( "Almost the car I want: " + $c.get( "car" ) );
    $c.missing();
end

Given the Car( "red", 1200, 160, true ), the second rule fires and produces:

Almost found the car I want: Car [colour=red, price=1200,
horsepower=160, abs=true]
missed: price < 1000 in Car

How to write the original rule is the last point. The actual user
interface could be simpler, but here is a basic fluent API:

public String makeRuleText(){
    RuleSet ruleSet = new RuleSet( "com.sample", "car" )
    .add(new BasicPattern( "car", Car.class ) )
    .add( new Constraint( "colour == \"red\"" ) )
    .add( new Constraint( "price < 1000" ) )
    .add( new Constraint( "horsepower >= 150") )
    .add( new Constraint( "abs == true" ) );
    return ruleSet.toString();
}

Cheers
Wolfgang


More information about the rules-users mailing list