Skip Headers

Oracle® Business Rules Language Reference 10g Release 3 (10.1.3) B15985-01 |
| Home | Solution Area | Contents | Index

Previous | Next |

# 1 Rules Programming Concepts

This chapter introduces Oracle Business Rules RL Language (RL Language) concepts, and includes the following sections:

- Starting the Oracle Business Rules RL Language Command-Line

- Introducing Rules and Rulesets

- Introducing Facts and RL Language Classes

- Understanding and Controlling Rule Firing

- Integrating RL Language Programs with Java Programs

- Building a Coin Counter Rules Program

## 1.1 Starting the Oracle Business Rules RL Language Command-Line

The Oracle Business Rules environment is implemented in a JVM or in a J2EE container by the classes supplied with `rl.jar`. Start the RL Language command-line interface using the following command:

    java -jar RuleDir/lib/rl.jar -p "RL> "

Where RuleDir is the path for the Oracle Business Rules installation. The `–p` option specifies the prompt.

The RL Language command-line interface provides access to an Oracle Business Rules RuleSession. The RuleSession is the API that allows Java programmers to access the RL Language in a Java application (the command-line interface uses a RuleSession internally).

You can run the program in Example 1-1 using the command-line interface by entering the text shown at the `RL>` prompt.

Example 1-1 Using the Command-Line Interface

    RL> println(1 + 2);
    3
    RL> final int low = -10;
    RL> final int high = 10;
    RL> println(low + high * high);
    90
    RL> exit;

See Also: Chapter 3, "Using the Command-line Interface" for more details and for a list of command-line options Chapter 4, "Using a RuleSession" for details on Oracle Business Rules RuleSession API

## 1.2 Introducing Rules and Rulesets

An RL Language ruleset provides a namespace, similar to a Java package, for RL classes, functions, and rules. In addition, you can use rulesets to partially order rule firing. A ruleset may contain executable actions, may include or contain other rulesets, and may import Java classes and packages.

An RL Language rule consists of rule conditions, also called fact-set-conditions, and an action-block or list of actions. Rules follow an if-then structure with rule conditions followed by rule actions.

Example 1-2 shows a program that prints, "Hello World." This example demonstrates a program that contains a single top-level action in the default ruleset (named `main`). Example 1-2 contains only an action, and does not define a rule, so the action executes immediately at the command-line.

Example 1-2 Hello World Programming Example

    RL> println("Hello World");Hello World
    RL>

See Also: Understanding and Controlling Rule Firing for details on rule firing

### 1.2.1 Rule Conditions

A rule condition is a component of a rule that is composed of conditional expressions that refer to facts.

In the following example the conditional expression refers to a fact (`Driver` instance `d1`), followed by a test that the fact's data member, `age`, is less than 16.

    if (fact Driver d1 && d1.age < 16)

Example 1-3 shows the complete rule, written in RL Language (the rule includes a rule condition and a rule action).

The Oracle Rules Engine activates a rule whenever there is a combination of facts that makes the rule's conditional expression true. In some respects, a rule condition is like a query over the available facts in the Oracle Rules Engine, and for every row that returns from the query, the rule activates.

Note: Rule activation is not the same as rule firing.

Example 1-3 Defining a Driver Age Rule

    RL> rule driverAge{
         if (fact Driver d1 && d1.age < 16)
         {
            println("Invalid Driver");
         }
    }

### 1.2.2 Rule Actions

A rule action is activated if all of the rule conditions are satisfied. There are several kinds of actions that a rule's action-block might perform. For example, an action in the rule's action-block can add new facts by calling the assert function or remove facts by calling the retract function. An action can also execute a Java method or perform an RL Language function (Example 1-3 uses the `println` function). Using actions, you can call functions that perform a desired task associated with a pattern match.

## 1.3 Introducing Facts and RL Language Classes

This section describes Oracle Business Rules facts and includes the following sections:

- What Are Facts?

- Adding Facts to Working Memory with Assert

- Using RL Language Classes as Facts

- Using Java Classes as Facts

### 1.3.1 What Are Facts?

Oracle Business Rules facts are asserted objects. For Java objects, a fact is a shallow copy of the object, meaning that each property is cloned, if possible, and if not, then the fact is a copy of the Java object reference.

In RL Language, a Java object is an instance of a Java class and an RL Object is an instance of an RL Language class. You can use Java classes in the classpath or you can define and use RL Language classes in a ruleset. You can also declare additional properties that are associated with the existing properties or methods of a Java class using a fact class declaration. You can hide properties of a Java class that are not needed in facts using a fact class declaration.

An RL Language class is similar to a Java Bean without methods. An RL class contains set of named properties. Each property has a type that is either an RL class, a Java object, or a primitive type.

Using Oracle Business Rules, you typically use Java classes, including JAXB generated classes that support the use of XML, to create rules that examine the business objects in a rule enabled application, or to return results to the application. You typically use RL classes to create intermediate facts that can trigger other rules in the Oracle Rules Engine.

### 1.3.2 Adding Facts to Working Memory with Assert

Oracle Business Rules uses working memory to contain facts (facts do not exist outside of working memory). A RuleSession contains the working memory.

A fact in RL Language is an asserted instance of a class. Example 1-4 shows the assert function that adds an instance of the RL class `enterRoom` as a fact to working memory. A class that is the basis for asserted facts may be defined in Java or in RL Language.

In Example 1-4 the `sayHello` rule matches facts of type `enterRoom`, and for each such fact, prints a message. The action `new,` shown in the `assert` function, creates an instance of the `enterRoom` class.

In Example 1-4 the `run` function fires the `sayHello` rule.

Note: The RL Language `new` keyword extends the Java `new` functionality with the capability to specify initial values for properties.

Example 1-4 Matching a Fact Defined by an RL Language Class

    RL> class enterRoom { String who; }
    RL> assert(new enterRoom(who: "Bob"));
    RL> rule sayHello {
      if ( fact enterRoom ) {
        println("Hello " + enterRoom.who);
      }
    }
    RL> run();
    Hello Bob
    RL>

See Also: "Understanding and Controlling Rule Firing"

### 1.3.3 Using RL Language Classes as Facts

You can use RL Language classes in a rules program to supplement a Java application's object model, without having to change the application code for the Java application that supplies Java Objects.

Example 1-5 shows the `goldCust` rule uses a Java class containing customer data, `cust`; the rule's action asserts an instance of the `GoldCustomer` RL class, representing a customer that spends more than 500 dollars in a three month period. The Java `Customer` class includes a method `SpentInLastMonths` that is supplied an integer representing a number of months of customer data to add.

Example 1-5 goldCust Rule

    rule goldCust {
      if (fact Customer cust && cust.SpentInLastMonths(3) > 500 ){
                   assert (new GoldCustomer(cust: cust));
      }
    }

Example 1-6 shows the `goldDiscount` rule uses the RL fact `GoldCustomer` to infer that if a customer spent $500 within the past 3 months, then the customer is eligible for a 10% discount.

Example 1-6 goldDiscount Rule

    rule goldDiscount {
       if (fact Order ord & fact GoldCustomer(cust: ord.customer) )
           {
                ord.discount = 0.1;
                assert(ord);
           }
    }

Example 1-7 shows the declaration for the `GoldCustomer` RL class (this assumes that you also have the `Customer` class available in the classpath).

Example 1-7 Declaring an RL Language Class

    class GoldCustomer {
     Customer cust;
    }

See Also: "Adding Facts to Working Memory with Assert"

### 1.3.4 Using Java Classes as Facts

You can use asserted Java objects as facts in an RL Language program. You are not required to explicitly define or declare the Java classes. However, you must include the Java classes in the classpath when you run the program. This lets you use the Java classes in rules, and allows a rules program to access and use the public attributes, public methods, and bean properties defined in the Java class (bean properties are preferable for some applications because the Oracle Rules Engine can detect that a Java object supports `PropertyChangeListener`; in this case it uses that mechanism to be notified when the object changes).

In addition, Fact class declarations can fine tune the properties available to use in an RL program, and may be required for certain multiple inheritance situations.

When you work with Java classes, using the `import` statement lets you omit the package name (see Example 1-8).

Example 1-8 Sample Java Fact with Import

    ruleset main
    {
      import example.Person;
      import java.util.*;
      rule hasNickNames
      {
        if (fact Person p && ! p.nicknames.isEmpty() )
        {
           // accessing properties as fields:
           println(p.firstName + " " + p.lastName + " has nicknames:");
           Iterator i = p.nicknames.iterator();
           while (i.hasNext())
           {
              println(i.next());
           }
        }
    }

See Also: "Fact Class Declarations" "Import Statement"

## 1.4 Understanding and Controlling Rule Firing

This section covers the following topics:

- Rule Activation and the Agenda

- Watching Facts, Rules, and Rule Activations

- Ordering Rule Firing

### 1.4.1 Rule Activation and the Agenda

The Oracle Rules Engine matches facts against the rule conditions (fact-set-conditions) of all rules as the state of working memory changes. The Oracle Rules Engine only checks for matches when the state of working memory changes, typically when a fact is asserted or retracted. A group of facts that makes a given rule condition true is called a fact set row. A fact set is a collection of all the fact set rows for a given rule. Thus a fact set consists of the facts that match the rule conditions for a rule. For each fact set row in a fact set, an activation, consisting of a fact set row and a reference to the rule is added to the agenda (the agenda contains the complete list of activations).

Figure 1-1 shows a RuleSession with an agenda containing activations in working memory.

Figure 1-1 RuleSession with Working Memory and the Agenda Containing Activations

Description of "Figure 1-1 RuleSession with Working Memory and the Agenda Containing Activations"

The run, runUntilHalt, and step functions execute the activations on the agenda, that is, these commands fire the rules (use the step command to fire a specified number of activations).

Rules fire when the Oracle Rules Engine removes activations, by popping the activations off the agenda and performing the rule's actions.

The Oracle Rules Engine may remove activations without firing a rule if the rule conditions are no longer satisfied. For example, if the facts change or the rule is cleared then activations may be removed without firing. Further, the Oracle Rules Engine removes activations from the agenda when the facts referenced in a fact set row are modified or the facts are retracted, such that they no longer match a rule condition (and this can also happen in cases where new facts are asserted, when the `!` operator applies).

Note the following concerning rule activations:

1. Activations are created, and thus rules fire only when facts are asserted, modified, or retracted (otherwise, the rules would fire continuously).

2. If a rule asserts a fact that is mentioned in the rule condition, and the rule condition is still true, then a new activation is added back to the agenda and the rule fires again (in this case the rule would fire continuously). This behavior is often a bug. Avoid this by adding additional limiting details in the rule condition.

3. The actions associated with a rule firing can change the set of activations on the agenda, by modifying facts, asserting facts, or retracting facts, and this can change the next rule to fire.

4. Rules fire sequentially, not in parallel.

See Also: Ordering Rule Firing

### 1.4.2 Watching Facts, Rules, and Rule Activations

You can use the functions `watchActivations`, `watchFacts`, `watchRules`, and `showFacts` to help write and debug RL Language programs.

This section covers the following topics:

- Watching and Showing Facts in Working Memory

- Watching Activations and Rule Firing

#### 1.4.2.1 Watching and Showing Facts in Working Memory

Example 1-9 shows the `watchFacts` function that prints information about facts entering and leaving working memory.

As shown in Example 1-9, the `watchFacts` function prints `==>` when a fact is asserted. Each fact is assigned a short identifier, beginning with `f-`, so that the fact may be referenced. For example, activations include a reference to the facts that are passed to the rule actions.

In Example 1-9, notice that the program uses the default ruleset `main`. This ruleset contains the `enterRoom` class.

Example 1-9 Using watchFacts with enterRoom Facts

    RL> watchFacts();
    RL>  class enterRoom {String who;}
    RL> assert(new enterRoom(who: "Rahul"));
     ==> f-1 main.enterRoom(who : "Rahul")
    RL> assert(new enterRoom(who: "Kathy"));
     ==> f-2 main.enterRoom(who : "Kathy")
    RL> assert(new enterRoom(who: "Tom"));
     ==> f-3 main.enterRoom(who : "Tom")
    RL>

You can use `showFacts` to show the current facts in working memory. Example 1-10 shows that the Oracle Rules Engine asserts the initial-fact, `f-0` (the Oracle Rules Engine uses this fact internally).

Example 1-10 Show Facts in Working Memory

    RL> showFacts();f-0   initial-fact()f-1   main.enterRoom(who : "Rahul")f-2   main.enterRoom(who : "Kathy")f-3   main.enterRoom(who : "Tom")For a total of 4 facts.

Use retract to remove facts from working memory, as shown in Example 1-11. When `watchFacts` is enabled, the Oracle Rules Engine prints <`==` when a fact is retracted.

Example 1-11 Retracting Facts from Working Memory

    RL> watchFacts();
    RL> retract(object(2));
     <== f-2 main.enterRoom(who : "Kathy")
    RL> showFacts();
    f-0   initial-fact()
    f-1   main.enterRoom(who : "Rahul")
    f-3   main.enterRoom(who : "Tom")
    For a total of 3 facts.

#### 1.4.2.2 Watching Activations and Rule Firing

The `watchActivations` function monitors the Oracle Rules Engine and prints information about rule activations entering and leaving the agenda. The `watchRules` function prints information about rules firing.

Example 1-12 shows how `run` causes the activations to fire. Notice that Rahul is greeted last even though he entered the room first (this is due to the firing order).

Note: Activations may be removed from the agenda before they are fired if their associated facts no longer make the condition true.

Example 1-12 Using WatchActivations and WatchRules

    RL> clear;
    RL> class enterRoom {String who;}
    RL> assert(new enterRoom(who: "Rahul"));
    RL> assert(new enterRoom(who: "Kathy"));
    RL> assert(new enterRoom(who: "Tom"));
    RL> watchActivations();
    RL> rule sayHello {
    if (fact enterRoom) {
          println("Hello " + enterRoom.who);
       }
    }
    ==> Activation: main.sayHello :  f-1
    ==> Activation: main.sayHello :  f-2
    ==> Activation: main.sayHello :  f-3
    RL> watchRules();
    RL> run();
    Fire 1 main.sayHello f-3
    Hello Tom
    Fire 2 main.sayHello f-2
    Hello Kathy
    Fire 3 main.sayHello f-1
    Hello Rahul
    RL>

### 1.4.3 Ordering Rule Firing

To understand the ordering algorithm for firing rule activations on the agenda, we introduce the ruleset stack. Each `RuleSession` includes one ruleset stack. The `RuleSession`'s ruleset stack contains the top of the stack, called the focus ruleset, and any non focus rulesets that are also on the ruleset stack. You place additional rulesets on the ruleset stack using either the pushRuleset or setRulesetStack built-in functions. You can manage the rulesets on the ruleset stack with the clearRulesetStack, popRuleset, and setRulesetStack functions. In this case, the focus of the ruleset stack is the current top ruleset in the ruleset stack (see Example 1-13).

Example 1-13 Ruleset Stack - Picture

    RuleSet Stack

               Focus Ruleset  -->     Top_Ruleset
                                      Next_down_Ruleset
                                      Lower_Ruleset
                                      Bottom_Ruleset

When activations are on the agenda, the Oracle Rules Engine fires rules when run, runUntilHalt, or step executes. The Oracle Rules Engine sequentially selects a rule activation from all of the activations on the agenda, using the following ordering algorithm:

1. The Oracle Rules Engine selects all the rule activations for the focus ruleset, that is the ruleset at the top of the ruleset stack (see the pushRuleset and setRulesetStack built-in functions).

2. Within the set of activations associated with the focus ruleset, rule priority specifies the firing order, with the higher priority rule activations selected to be fired ahead of lower priority rule activations (the default priority level is 0).

3. Within the set of rule activations of the same priority, within the focus ruleset, the most recently added rule activation is the next rule to fire. However, note that in some cases multiple activations may be added to the agenda at the same time, the ordering for such activations is not defined.

4. When all of the rule activations in the current focus fire, the Oracle Rules Engine pops the ruleset stack, and the process returns to Step 1, with the current focus.

If a set of rules named R1 must all fire before any rule in a second set of rules named R2, then you have two choices:

- Use a single ruleset and set the priority of the rules in R1 higher than the priority of rules in R2.

- Use two rulesets R1 and R2, and push R2 and then R1 on the ruleset stack.

Generally, using two rulesets with the ruleset stack is more flexible than using a single ruleset and setting the priority to control when rules fire. For example if some rule R in R1 must trigger a rule in R2 before all rules in R1 fire, a return in R pops the ruleset stack and allows rules in R2 to fire.

If execution must alternate between two sets of rules, for example, rules to produce facts and rules to consume facts, it is easier to alternate flow with different rulesets than by using different priorities.

Example 1-14 shows that the priority of the `keepGaryOut` rule is set to high, this is higher than the priority of the `sayHello` rule (the default priority is 0). If the activations of both rules are on the agenda, the higher priority rule fires first. Notice that just before calling `run`, `sayHello` has two activations on the agenda. Because `keepGaryOut` fires first, it retracts the `enterRoom(who: "Gary")` fact, which removes the corresponding `sayHello` activation, resulting in only one `sayHello` firing.

The rule shown in Example 1-14 illustrates two additional RL Language features.

1. The `fact` operator, also known as a fact set pattern, uses the optional `var` keyword to define a variable, in this case the variable `g`, that is bound to the matching facts.

2. You can remove facts in working memory using the `retract` function.

Example 1-14 Using Rule Priority with keepGaryOut Rule

    RL> final int low = -10;
    RL> final int high = 10;
    RL> rule keepGaryOut {
      priority = high;
      if (fact enterRoom(who: "Gary") var g) {
        retract(g);
      }
    }
    RL> assert(new enterRoom(who: "Gary"));
    ==> f-4 main.enterRoom(who: "Gary")
    ==> Activation: main.sayHello : f-4
    ==> Activation: main.keepGaryOut : f-4
    RL> assert(new enterRoom(who: "Mary"));
    ==> f-5 main.enterRoom(who: "Mary")
    ==> Activation: main.sayHello : f-5
    RL> run();
    Fire 1 main.keepGaryOut f-4
    <== f-4 main.enterRoom(who: "Gary")
    <== Activation: main.sayHello : f-4
    Fire 2 main.sayHello f-5
    Hello Mary
    RL>

Example 1-15 shows the `sayHello` rule that includes a condition that matches the asserted `enterRoom` fact; this match adds an activation to the agenda. Example 1-15 demonstrates the following RL Language programming features.

1. The Oracle Rules Engine matches facts against the rule conditions (fact-set-conditions) of all rules as the state of working memory changes. Thus, it does not matter whether facts are asserted before the rule is defined, or after.

2. The run function processes any activations on the agenda. No activations on the agenda are processed before calling run.

Example 1-15 enterRoom Class with sayHello Rule

    RL> class enterRoom { String who; }
    RL> rule sayHello {
      if ( fact enterRoom ) {
        println("Hello " + enterRoom.who);
      }
    }
    RL> assert(new enterRoom(who: "Bob"));
    RL> run();
    Hello Bob
    RL>

Notes for ordering rule firing.

1. When you use the `return` action, this changes the behavior for firing rules. A `return` action in a rule pops the ruleset stack, so that execution continues with the activations on the agenda that are from the ruleset that is currently at the top of the ruleset stack.

   If rule execution was initiated with either the run or step functions, and a return action pops the last ruleset from the ruleset stack, then control returns to the caller of the run or step function.

   If rule execution was initiated with the runUntilHalt function, then a return action does not pop the last ruleset from the ruleset stack. The last ruleset is popped with runUntilHalt when there are not any activations left. The Oracle Rules Engine then waits for more activations to appear. When they do, it places the last ruleset on the ruleset stack before resuming ruleset firing.

2. Rule priority is only applicable within rules in a given ruleset. Thus, the priority of rules in different rulesets are not comparable.

## 1.5 Integrating RL Language Programs with Java Programs

This section describes integrating RL Language programs with Java programs. This section covers the following topics:

- Using Java Beans Asserted as Facts

- Using RuleSession Objects in Java Applications

See Also: "Invoking Rules" in Chapter 3 in the Oracle Business Rules User's Guide

### 1.5.1 Using Java Beans Asserted as Facts

Example 1-16 shows the Java source for a simple bean. Use the `javac` command to compile the bean, `example.Person` shown in Example 1-16 into a directory tree.

The following shows how an RL Language command-line can be started that can access this Java bean:

    java -classpath RuleDir/lib/rl.jar;BeanPath oracle.rules.rl.session.CommandLine -p "RL> "

Where BeanPath is the classpath component to any supplied Java Bean classes.

Example 1-16 Java Source for Person Bean Class

    package example;
    import java.util.*;
    public class Person
    {
      private String firstName;
      private String lastName;
      private Set nicknames = new HashSet();

      public Person(String first, String last, String[] nick) {
        firstName = first; lastName = last;
        for (int i = 0; i < nick.length; ++i)
          nicknames.add(nick[i]);
      }
      public Person() {}
      public String getFirstName() {return firstName;}
      public void setFirstName(String first) {firstName = first;}
      public String getLastName() {return lastName;}
      public void setLastName(String last) {lastName = last;}
      public Set getNicknames() {return nicknames;}
    }

Example 1-17 shows how the RL Language command-line can execute an RL Language program that uses `example.Person`. The `import` statement, as in Java, allows a reference to the `Person` class using "`Person`" instead of "`example.Person`". Rules reference the `Person` bean class and its properties and methods. In order to create a `Person` fact you must `assert` a Java `Person` bean.

Example 1-17 uses the `new` operator to create an array of `Person` objects, named `people`. The `people` array is declared final so that `reset` does not create more `people`. The `numPeople` variable is not declared `final` so that `reset` re-invokes the `assertPeople` function and re-asserts the `Person` facts using the existing `Person` objects.

Example 1-17 Ruleset Using Person Bean Class

    ruleset main
    {
      import example.Person;
      import java.util.*;
      rule hasNickNames
      {
        if (fact Person(nicknames: var nns) p && !nns.isEmpty())
        {
                 // accessing properties as fields:
           println(p.firstName + " " + p.lastName + " has nicknames:");
           Iterator i = nns.iterator();
           while (i.hasNext())
           {
              println(i.next());
           }
        }
    }
    rule noNickNames
    {
       if fact Person(nicknames: var nns) p && nns.isEmpty()
       {
              // accessing properties with getters:
       println(p.getFirstName() + " " + p.getLastName() + " does not have nicknames");
       }
    }
      final Person[] people = new Person[] {
    new Person("Robert", "Smith", new String[] { "Bob", "Rob" }), // using constructor
    new Person(firstName: "Joe", lastName: "Schmoe") // using attribute value pairs
    };

    function assertPeople(Person[] people) returns int
    {
       for (int i = 0; i < people.length; ++i) {
          assert(people[i]);
    }
      return people.length;
      }
      int numPeople = assertPeople(people);
      run();
    }

Note the following when working with Java beans as facts:

1. The `fact` operator can include a pattern that matches or retrieves the bean properties. The properties are defined by getter and setter methods in the bean class.

2. The `new` operator can include a pattern that sets property values after invoking the default no-argument constructor, or can pass arguments to a user-defined constructor.

3. Outside of the `fact` and `new` operators, the bean properties may be referenced or updated using getter and setter methods, or using the property name as if it were a field.

4. If a bean has both a property and a field with the same name, then the field cannot be referenced in RL Language.

If Example 1-18 executes using the same `RuleSession` following the execution of Example 1-17, the output is identical to the Example 1-17 results (both `person` facts are reasserted).

Note: The RL Language command-line interpreter internally creates a `RuleSession` when it starts up (and when you use the `clear` command).

Example 1-18 Using Reset with a RuleSession

    reset();
    run();

### 1.5.2 Using RuleSession Objects in Java Applications

Java programs can use the `RuleSession` interface to execute rulesets, invoke RL Language functions passing Java objects as arguments, and redirect RL Language `watch` and `println` output. Example 1-19 and Example 1-20 each contain a Java program fragment that uses a `RuleSession` that prints "hello world". Like many Java program fragments, these examples are also legal RL Language programs.

The RL Language environment provides multiple rule sessions. Each rule session can be used by multiple threads, but rules are fired by a single thread at a time.

Each rule RuleSession has its own copy of facts and rules. To create a fact from a Java Object, use a call such as:

    rs.callFunctionWithArgument("assert", Object;);

To create a rule, a function, or an RL Language class, define a string containing a ruleset, and use the `executeRuleset` method.

Example 1-19 Using a RuleSession Object with callFunctionWithArgument

    import oracle.rules.rl.*;
    try {
      RuleSession rs = new RuleSession();
      rs.callFunctionWithArgument("println", "hello world");
    } catch (RLException rle) {
       System.out.println(rle);
    }

Example 1-20 Using a RuleSession with ExecuteRuleset

    import oracle.rules.rl.*;
    try {
       RuleSession rs = new RuleSession();
       String rset =
         "ruleset main {" +
         " function myPrintln(String s) {" +
         " println(s);" +
         " }" +
         "}";
       rs.executeRuleset(rset);
       rs.callFunctionWithArgument("myPrintln", "hello world");
    } catch (RLException rle) {
      System.out.println(rle);
    }

## 1.6 Building a Coin Counter Rules Program

This section shows a sample that uses RL Language to solve a puzzle:

How many ways can 50 coins add up to $1.50?

The rules program that solves this puzzle illustrates an important point for rule-based programming; knowledge representation, that is, the fact classes that you select, can be the key design issue. It is often worthwhile to write procedural code to shape your data into a convenient format for the rules to match and process.

To use this example, first copy the RL Language program shown in Example 1-22 to a file named `coins.rl`. You can include this from the RL Language command-line using the `include` command. Before you include the coins program, use the `clear;` command to erase everything in the current rule session, as follows:

    RL> clear;
    RL> include file:coins.rl;
    RL>

Example 1-21 shows the debugging functions that show the count coins sample facts, activations, and rules for the coin counter. All facts are asserted, and activations for all solutions are placed on the agenda. Notice that the facts are matched to the rule condition as they are generated by `populate_facts`, and that `find_solution` merely prints the matches.

Example 1-21 Using Debugging Functions with Coins Example

    RL> watchFacts();
    RL> watchActivations();
    RL> watchRules();
    RL> reset();
    RL> showActivations();
    RL> run();
    The rule is fired for each activation, printing out the solutions
    RL>

In Example 1-22, the keyword `final` in front of a global variable definition such as `coinCount` and `totalAmount` marks that variable as a constant, as in Java. You can reference constants in rule conditions, but you cannot reference variables in rule conditions.

In RL Language, you must initialize all variables. The initialization expression for a `final` variable is evaluated once when the variable is defined. The initialization expression for a non-final variable is evaluated when the variable is defined, and again each time the `reset` function is called. Because the `reset` function retracts all facts from working memory, it is good practice to `assert` initial facts in a global variable initialization expression, so that the facts are re-asserted when `reset` is called.

Example 1-22 illustrates how to use global variable initialization expressions. The `initialized` global variable is initialized with the `populate_facts` function. This function is re-executed whenever `reset` is called. The `populate_facts` function has a `while` loop nested within a `for` loop. The `for` loop iterates over an array of coin denomination Strings. For each denomination, the `while` loop asserts a fact that expresses a count and a total that does not exceed the total amount of $1.50. For example, for half dollars:

    coin(denomination "half-dollar", count:0, amount:0)
    coin(denomination "half-dollar", count:1, amount:50)
    coin(denomination "half-dollar", count:2, amount:100)
    coin(denomination "half-dollar", count:3, amount:150)

With such facts in working memory, the rule `find_solution` matches against each denomination with a condition that requires that the counts sum to `coinCount` and the amounts sum to `totalAmt`. The `run` function fires the `find_solutions` activations.

Example 1-22 Count Coins Program Source

    final int coinCount = 50;
    final int totalAmt = 150;
    final String[] denominations = new String[]
    {"half-dollar" , "quarter", "dime", "nickel", "penny" };
    class coin {
       String denomination;
       int count;
       int amount;
    }
    function populate_facts() returns boolean
    {
       for (int i = 0; i < denominations.length; ++i) {
          String denom = denominations[i];
          int count = 0;
          int total = 0;
          int amount = 0;
          if (denom == "half-dollar" ) { amount = 50; }
          else if (denom == "quarter" ) { amount = 25; }
          else if (denom == "dime" ) { amount = 10; }
          else if (denom == "nickel" ) { amount = 5; }
          else { amount = 1; }      while (total <= totalAmt && count <= coinCount)
          {
           assert(new coin(denomination: denom,
              count : count,
              amount : total));
              total += amount;
              count ++;
         }
       }
    return true;
    }
    boolean initialized = populate_facts();
    rule find_solution
    {
       if(fact coin(denomination: "penny") p
       && fact coin(denomination: "nickel") n
       && fact coin(denomination: "dime") d
       && fact coin(denomination: "quarter") q
       && fact coin(denomination: "half-dollar") h
       && p.count + n.count + d.count + q.count + h.count == coinCount
       && p.amount + n.amount + d.amount + q.amount + h.amount == totalAmt)
       {
          println("Solution:"
                 + " pennies=" + p.count
                 + " nickels=" + n.count
                 + " dimes=" + d.count
                 + " quarters=" + q.count
                 + " half-dollars=" + h.count
          );
       }
    }
    run();