6. Making Your Own Rules

6.1. Introducing defrules

Now that we've learned how to populate Jess's working memory, we can answer the obvious question: what is it good for? The answer is that defquerys can search it to find relationships between facts, and defrules can take actions based on the contents of one or more facts.

A Jess rule is something like an if... then statement in a procedural language, but it is not used in a procedural way. While if... then statements are executed at a specific time and in a specific order, according to how the programmer writes them, Jess rules are executed whenever their if parts (their left-hand-sides or LHSs) are satisfied, given only that the rule engine is running. This makes Jess rules less deterministic than a typical procedural program. See the chapter on the Rete algorithm for an explanation of why this architecture can be many orders of magnitude faster than an equivalent set of traditional if... then statements.

In this chapter we're going to make a lot of use of a "person" template that looks like this:
    
Jess> (deftemplate person (slot firstName) (slot lastName) (slot age))
Rules are defined in Jess using the defrule construct. A very simple rule looks like this:
    
Jess> (defrule welcome-toddlers
    "Give a special greeting to young children"
    (person {age < 3})
    =>
    (printout t "Hello, little one!" crlf))

This rule has two parts, separated by the "=>" symbol (which you can read as "then".) The first part consists of the LHS pattern (person {age < 3}). The second part consists of the RHS action, the call to println. If you're new to Jess, it can be hard to tell the difference due to the LISP-like syntax, but the LHS of a rule consists of patterns which are used to match facts in the working memory, while the RHS contains function calls.
The LHS of a rule (the "if" part) consists of patterns that match facts, NOT function calls. The actions of a rule (the "then" clause) are made up of function calls. The following rule does NOT work:

Jess> (defrule wrong-rule
  (eq 1 1)
  =>
  (printout t "Just as I thought,  1 == 1!" crlf))
This rule will NOT fire just because the function call (eq 1 1) would evaluate to true. Instead, Jess will try to find a fact in the working memory that looks like (eq 1 1). Unless you have previously asserted such a fact, this rule will NOT be activated and will not fire. If you want to fire a rule based on the evaluation of a function that is not related to a pattern, you can use the test CE.
Our example rule, then, will be activated when an appropriate (person) fact appears in the working memory. When the rule executes, or fires, a message is printed. Let's turn this rule into a complete program. The function watch all tells Jess to print some useful diagnostics as we enter our program.
    
Jess> (deftemplate person (slot firstName) (slot lastName) (slot age))
    TRUE
    Jess> (watch all)
    TRUE
    Jess>  (reset)
==> f-0 (MAIN::initial-fact)
TRUE
    Jess> (defrule welcome-toddlers
        "Give a special greeting to young children"
        (person {age < 3})
        =>
        (printout t "Hello, little one!" crlf))
welcome-toddlers: +1+1+1+t
TRUE
Jess>  (assert (person (age 2)))
==> f-1 (MAIN::person (firstName nil) (lastName nil) (age 2))
==> Activation: MAIN::welcome-toddlers :  f-1
<Fact-1>

Some of these diagnostics are interesting. We see first of all how issuing the reset command asserts the fact (initial-fact). You should always issue a reset command when working with rules. When the rule itself is entered, we see the line "+1+1+t". This tells you something about how the rule is interpreted by Jess internally (see The Rete Algorithm for more information.) When the fact (person (age 2)) is asserted, we see the diagnostic "Activation: MAIN::welcome-toddlers : f-1". This means that Jess has noticed that the rule welcome-toddlers has all of its LHS conditions met by the given list of facts ("f-1").

After all this, our rule didn't fire; why not? Jess rules only fire while the rule engine is running (although they can be activated while the engine is not running.) To start the engine running, we issue the run command.

Jess> (run)
FIRE 1 MAIN::welcome-toddlers f-1
Hello, little one!
<== Focus MAIN
1

As soon as we enter the run command, the activated rule fires. Since we have watch all, Jess prints the diagnostic FIRE 1 welcome-toddlers f-1 to notify us of this. We then see the output of the rule's RHS actions. The final number "1" is the number of rules that fired (it is the return value of the run command.) The run function returns when there are no more activated rules to fire.

What would happen if we entered (run) again? Nothing. A rule will be activated only once for a given set of facts; once it has fired, that rule will not fire again for the same list of facts. We won't print the message again until another toddler shows up.

Rules are uniquely identified by their name. If a rule named my-rule exists, and you define another rule named my-rule, the first version is deleted and will not fire again, even if it was activated at the time the new version was defined.

6.2. Simple patterns

A pattern is always set of parentheses including the name of the fact to be matched plus zero or more slot descriptions. There are now two kinds of slot descriptions in Jess: the new-style "simplified" syntax, and the old-style, more complex but more powerful syntax. New-style slot descriptions are enclosed in curly braces, like the one in our welcome-toddlers rule:


Jess> (defrule welcome-toddlers
    "Give a special greeting to young children"
    (person {age < 3})
    =>
    ((System.out) println "Hello, little one!"))
        

Old-style slot descriptions use parentheses instead of curly braces. The syntax allowed for the two kinds of descriptions are different. We'll talk mostly about the new simplified syntax in this section, and save most of the old-style syntax for the next section. However, there is one very easy and very important thing you can do with the old-style syntax that we'll need right away: you can declare a variable to refer to the contents of a slot. For example, look at the following pattern:

(person (age ?a) (firstName ?f) (lastName ?l))
        

This pattern will match any person fact. When the rule fires, Jess will assign the contents of the "age" slot to a variable "?a", the firstName slot to a variable "?f", and the lastName slot to "?l". You'll be able to use these variables elsewhere in the same rule, both on the left-hand side and on the right-hand side.

The simplified slot descriptions we'll talk about in this section are Java-like Boolean expressions using the following operators:

These operators are used in infix format, like Java operators. Within a simplified pattern, a symbol stands for the value of the slot in that pattern with the same name. The syntax is simple and a few examples will serve to document it. The first example matches any person who is between the ages of 13 and 19, inclusive:

            
Jess> (defrule teenager
    (person {age > 12 && age < 20} (firstName ?name))
    =>
    (printout t ?name " is " ?age " years old." crlf))
        

For each slot that you mention in a simplified slot desciption, a variable named after the slot is automatically created and is available on the right-hand side of the rule. We needed to declare a variable for the firstName slot ourselves, as it wasn't otherwise mentioned on the rule's LHS.

You can write tests that look at several slots at once:

            
Jess> (defrule same-first-and-last-name
    (person {firstName == lastName})
    =>
    (printout t ?firstName " " ?lastName " is a funny name!" crlf))
        

6.3. Patterns in Depth

The curly-brace notation we looked at in the previous section is a simplified way of writing patterns that fills many basic needs. But Jess actually supports a richer syntax that gives you more capabilities. One limitation of the curly-brace notation is that it can only be used with unordered facts. It is this richer syntax that we'll cover here. Whereas the simplified slot patterns use curly braces, the richer syntax uses parentheses to enclose slots.

As previously shown, you can specify a variable name for a field in any of a rule's patterns (but not the pattern's head). A variable matches any value in that position within a rule. For example, the rule:

Jess> (deftemplate coordinate (slot x) (slot y))
Jess> (defrule example-2
    (coordinate (x ?x) (y ?y))
    =>
    (printout t "Saw 'coordinate " ?x " " ?y "'" crlf))
            
will be activated once for every coordinate fact. The variables ?x and ?y matched in the pattern are available in the actions on the RHS of the same rule.

A slot descriptor can also include any number of tests to qualify what it will match. Tests follow the variable name and are separated from it and from each other by an and (&) or or (|) symbol. (The variable name itself is actually optional.) Tests can be:

Ampersands (&) represent logical "and", while pipes (|) represent logical "or." & has a higher precedence than |, so that the following
(foo ?X&:(oddp ?X)&:(< ?X 100)|0)
            
matches a foo fact with a single field containing either an odd number less than 100, or 0.

Here's an example of a rule that uses several kinds of tests:

Jess> (defrule example-3
  (not-b-and-c ?n1&~b ?n2&~c)
  (different ?d1 ~?d1)
  (same ?s ?s)
  (more-than-one-hundred ?m&:(> ?m 100))
  (red-or-blue red|blue)
  =>
  (printout t "Found what I wanted!" crlf))

The first pattern will match an ordered fact with head not-b-and-c with exactly two fields such that the first is not b and the second is not c. The second pattern will match any fact with head different and two fields such that the two fields have different values. The third pattern will match a fact with head same and two fields with identical values. The fourth pattern matches a fact with head more-than-one-hundred and a single field with a numeric value greater than 100. The last pattern matches a fact with head red-or-blue followed by either the symbol red or the symbol blue.

If you match to a defglobal with a pattern like (foo ?*x*), the match will only consider the value of the defglobal when the fact is asserted. Subsequent changes to the defglobal's value will not invalidate the match - i.e., the match does not reflect the current value of the defglobal, but only the value at the time the matching fact was asserted.

6.4. Matching in Multislots

Pattern matching in multislots (and in ordered facts, which are really just facts with a single multislot whose name is hidden) is similar to matching in regular slots. The main difference is that you may include separate clusters of tests for each field within a multislot. The number of clusters implicitly specifies the number of items in a matching multislot. So, for example, the grocery-list pattern in the following rule matches only grocery lists with exactly three items:


Jess> (defrule match-three-items
  (grocery-list ? ? ?)
  =>
  (printout t "Found a three-item list" crlf))
TRUE
Jess> (assert (grocery-list eggs milk bacon))
<Fact-0>
Jess> (run)
Found a three-item list
1

Note that, as shown here, you can match a field without binding it to a named variable by omitting the variable name and using just a question mark (?) as a placeholder.

You can match any number (zero or more) of fields in a multislot or ordered fact using a multifield. A multifield is just a variable constraint preceded by a '$' character. The matched items are used to construct a list, and the list is assigned to that variable:


Jess> (defrule match-whole-list
  (grocery-list $?list)
  =>
  (printout t "I need to buy " ?list crlf))
TRUE
Jess> (assert (grocery-list eggs milk bacon))
<Fact-0>
Jess> (run)
I need to buy (eggs milk bacon)
1

Multifields can be used in combination with other kinds of tests, and they're a very convenient way of saying "... and some other stuff." For example, this rule matches grocery lists containing bacon in any position. It does this by using two blank multifields: one to match all the items before bacon in the list, and the other (which in this case, will match zero items) to match all the items after.


Jess> (defrule match-list-with-bacon
  (grocery-list $? bacon $?)
  =>
  (printout t "Yes, bacon is on the list" crlf))
TRUE
Jess> (assert (grocery-list eggs milk bacon))
<Fact-0>
Jess> (run)
Yes, bacon is on the list
1

Finally, note that a multifield is not a special kind of variable. When a multifield $?list is matched, it's the variable ?list that receives the value.

6.5. Pattern bindings

Sometimes you need a handle to an actual fact that helped to activate a rule. For example, when the rule fires, you may need to retract or modify the fact. To do this, you use a pattern-binding variable:

Jess> (defrule example-5
  ?fact <- (a "retract me")
  =>
  (retract ?fact))
The variable (?fact, in this case) is bound to the particular fact that activated the rule.

Note that ?fact is a jess.Value object of type RU.FACT, not an integer. It is basically a reference to a jess.Fact object. You can convert an ordinary number into a FACT using the fact-id function. You can convert a FACT into an integer when necessary by using reflection to call the Fact.getFactId() function. The jess.Value.factValue() method can be called on a FACT Value to obtain the actual jess.Fact object from Java code. In Jess code, a fact-id essentially is a jess.Fact, and you can call jess.Fact methods on a fact-id directly:

Jess> (defrule example-5-1
  ?fact <- (initial-fact)
  =>
  (printout t (call ?fact getName) crlf))
TRUE
Jess> (reset)
TRUE
Jess> (run)
initial-fact
1

See the section on the jess.FactIDValue class for more information.

Note that once a fact is asserted, Jess will always use the same jess.Fact object to represent it, even if the original fact is modified. Therefore, you can store references to fact objects in the slots of other facts as a way of representing structured data.

6.6. More about regular expressions

Jess's new regular expression facility builds on Java's java.util.regex package. This section presents a few examples of how it works.
(defrule rule-1
  (foo /xy+z/)
  =>)

(defrule rule-2
  (foo a ?x&/d*ef/)
  (bar ?x)
  =>)
    

The first rule matches a "foo" fact with a single field of the form "xyz", "xyyz", "xyyyz"... The second rule matches a "foo" fact with two fields: the symbol "a" followed by a string or symbol of the form "ef", "def", "ddef"...; this string is bound to the variable ?x and matched to a field in the second pattern.

Note that the regular expressions are those defined by the java.util.regex package. If you try to use these functions, with JDK 1.3 or below, you will find that they will simply not be defined.

There is also a function regexp which can be used in procedural code. In this release, it just takes two arguments, a regular expression and a target string, and returns a boolean result.

6.7. Salience and conflict resolution

Each rule has a property called salience that is a kind of rule priority. Activated rules of the highest salience will fire first, followed by rules of lower salience. To force certain rules to always fire first or last, rules can include a salience declaration:

Jess> (defrule example-6
  (declare (salience -100))
  (command exit-when-idle)
  =>
  (printout t "exiting..." crlf))
Declaring a low salience value for a rule makes it fire after all other rules of higher salience. A high value makes a rule fire before all rules of lower salience. The default salience value is zero. Salience values can be integers, global variables, or function calls. See the set-salience-evaluation command for details about when such function calls will be evaluated.

The order in which multiple rules of the same salience are fired is determined by the active conflict resolution strategy. Jess comes with two strategies: "depth" (the default) and "breadth." In the "depth" strategy, the most recently activated rules will fire before others of the same salience. In the "breadth" strategy, rules fire in the order in which they are activated. In many situations, the difference does not matter, but for some problems the conflict resolution strategy is important. You can write your own strategies in Java; see the chapter on extending Jess with Java for details. You can set the current strategy with the set-strategy command.

Note that the use of salience is generally discouraged, for two reasons: first it is considered bad style in rule-based programming to try to force rules to fire in a particular order. Secondly, use of salience will have a negative impact on performance, at least with the built-in conflict resolution strategies.

You can see the list of activated, but not yet fired, rules with the agenda command.

6.8. The 'and' conditional element.

Any number of patterns can be enclosed in a list with and as the head. The resulting pattern is matched if and only if all of the enclosed patterns are matched. By themselves, and groups aren't very interesting, but combined with or and not conditional elements, they can be used to construct complex logical conditions.

The entire left hand side of every rule and query is implicitly enclosed in an and conditional element.

6.9. The 'or' conditional element.

Any number of patterns can be enclosed in a list with or as the head. The resulting pattern is matched if one or more of the patterns inside the or are matched. If more than one of the subpatterns are matched, the or is matched more than once:

Jess> (defrule or-example-1
   (or (a) (b) (c))
   =>)
Jess> (assert (a) (b) (c))
Jess> (printout t (run) crlf)
3

An and group can be used inside of an or group, and vice versa. In the latter case, Jess will rearrange the patterns so that there is a single or at the top level. For example, the rule
(defrule or-example-2a
    (and (or (a)
             (b))
         (c))
      =>)
will be automatically rearranged to
(defrule or-example-2b
    (or (and (a) (c))
        (and (b) (c)))
    =>)
DeMorgan's second rule of logical equivalence, namely
(not (or (x) (y)))  => (and (not (x)) (not (y)))
will be used when necessary to hoist an or up to the top level.

Note that if the right hand side of a rule uses a variable defined by matching on the left hand side of that rule, and the variable is defined by one or more branches of an or pattern but not all branches, then a runtime error may occur.

6.10. The 'not' conditional element.

Any single pattern can be enclosed in a list with not as the head. In this case, the pattern is considered to match if a fact (or set of facts) which matches the pattern is not found. For example:

Jess> (defrule example-7
   (person ?x)
   (not (married ?x))
   =>
   (printout t ?x " is not married!" crlf))
Note that a not pattern cannot define any variables that are used in subsequent patterns (since a not pattern does not match any facts, it cannot be used to define the values of any variables!) You can introduce variables in a not pattern, so long as they are used only within that pattern; i.e,

Jess> (defrule no-odd-numbers
   (not (number ?n&:(oddp ?n)))
   =>
   (printout t "There are no odd numbers." crlf))
Similarly, a not pattern can't have a pattern binding.

A not CE is evaluated only when either a fact matching it exists, or when the pattern immediately before the not on the rule's LHS is evaluated. If a not CE is the first pattern on a rule's LHS, or is the the first the pattern in an and group, or is the only pattern on a given branch of an or group, the pattern (initial-fact) is inserted to become this important preceding pattern. Therefore, the fact (initial-fact) created by the reset command is necessary to the proper functioning of some not patterns. For this reason, it is especially important to issue a reset command before attempting to run the rule engine when working with not patterns.

Multiple not CEs can be nested to produce some interesting effects (see the discussion of the exists CE).

The not CE can be used in arbitrary combination with the and and or CEs. You can define complex logical structures this way. For example, suppose you want a rule to fire once if for every fact (a ?x), there is a fact (b ?x). You could express that as

Jess> (defrule forall-example
  (not (and (a ?x) (not (b ?x))))
  =>)
i.e., "It is not true that for some ?x, there is an (a ?x) and no (b ?x)". This is actually how the the forall CE is implemented.

6.11. The 'exists' conditional element.

A pattern can be enclosed in a list with exists as the head. An exists CE is true if there exist any facts that match the pattern, and false otherwise. exists is useful when you want a rule to fire only once, although there may be many facts that could potentially activate it.

Jess> (defrule exists-demo
    (exists (honest ?))
    =>
    (printout t "There is at least one honest man!" crlf))
        
If there are any honest men in the world, the rule will fire once and only once.

exists may not be combined in the same pattern with a test CE.

Note that exists is precisely equivalent to (and in fact, is implemented as) two nested not CEs; i.e., (exists (A)) is the same as (not (not (A))). It is rather common for people to write something like "(not (exists (A)))," but this is just a very inefficient way to write (not (A)).

6.12. The 'test' conditional element.

A pattern with test as the head is special; the body consists not of a pattern to match against the working memory but of a Boolean function. The result of evaluating this function determines whether the pattern matches. A test pattern fails if and only if the function evaluates to the symbol FALSE; if it evaluates to TRUE or any other value, the pattern with "match." For example:

Jess> (deftemplate person (slot age))
Jess> (defrule example-8
   (test (eq 4 (+ 2 2)))
   =>
   (printout t "2 + 2 is 4!" crlf))
Note that a test pattern, like a not, cannot contain any variables that are not bound before that pattern. test and not may be combined:
  (not (test (eq ?X 3)))
is equivalent to:
  (test (neq ?X 3))

A test CE is evaluated every time the preceding pattern on the rule's LHS is evaluated. Therefore the following two rules are precisely equivalent in behaviour:

Jess> (defrule rule_1
  (foo ?X)
  (test (> ?X 3))
  =>)

Jess> (defrule rule_2
  (foo ?X&:(> ?X 3))
  =>)

For rules in which a test CE is the first pattern on the LHS or the first pattern in a branch of an or CE, the pattern (initial-fact) is inserted to become the "preceding pattern" for the test. The fact (initial-fact) is therefore also important for the proper functioning of the test conditional element; the caution about reset in the preceding section applies equally to test.

6.12.1. Time-varying method returns

One useful property of the test CE is that it's the only valid place to put tests whose results might change without the contents of any slot changing. For example, imagine that you've got two Java classes, A and B, and that A has a method contains which takes a B as an argument and returns boolean. Further, imagine that for any given B object, the return value of contains will change over time. Finally, imagine that you've defclassed both these classes and are writing rules to work with them. Under these circumstances, a set of patterns like this:
(A (OBJECT ?a))
(B (OBJECT ?b&:(?a contains ?b)))
is incorrect. If the return value of contains changes, the match will be invalidated and Jess's internal data structures may be corrupted. In particular, this kind of construct tends to cause memory leaks.

The correct way to express this same set of patterns is to use the test conditional element, like this:
(A (OBJECT ?a))
(B (OBJECT ?b))
(test (?a contains ?b))
The function contains is now guaranteed to be called at most once for each combination of target and argument, and so any variation in return value will have no impact.

6.12.2. When should I use test?

The test conditional element is greatly overused. Using test is always less efficient than adding the identical test to a slot on a preceding pattern. You should never write something like:


Jess>  (defrule bad-test-usage
    (foo ?x)
    (test (< ?x 3)
    =>)
            

Instead, always prefer


Jess>  (defrule better
    (foo ?x&:(< ?x 3))
    =>)
            

Use test only when there is no preceding pattern, or with time-varying return values as described previously.

6.13. The 'logical' conditional element.

The logical conditional element lets you specify logical dependencies among facts. All the facts asserted on the RHS of a rule become dependent on the matches to the logicalpatterns on that rule's LHS. If any of the matches later become invalid, the dependent facts are retracted automatically. In this simple example, a single fact is made to depend on another single fact:

Jess> (defrule rule-1
  (logical (faucet-open))
  =>
  (assert (water-flowing)))
TRUE
Jess> (assert (faucet-open))
<Fact-0>
Jess> (run)
1
Jess> (facts)
f-0   (MAIN::faucet-open)
f-1   (MAIN::water-flowing)
For a total of 2 facts in module MAIN.
Jess> (watch facts)
TRUE
Jess> (retract (fact-id 0))
<== f-0 (MAIN::faucet-open)
<== f-1 (MAIN::water-flowing)
TRUE

The (water-flowing) fact is logically dependent on the (faucet-open) fact, so when the latter is retracted, the former is removed, too.

A fact may receive logical support from multiple sources -- i.e., it may be asserted multiple times with a different set of logical supports each time. Such a fact isn't automatically retracted unless each of its logical supports is removed.

If a fact is asserted without explicit logical support, it is said to be unconditionally supported. If an unconditionally supported fact also receives explicit logical support, removing that support will not cause the fact to be retracted.

If one or more logical CEs appear in a rule, they must be the first patterns in that rule; i.e., a logical CE cannot be preceded in a rule by any other kind of CE.

Definstance facts are no different than other facts with regard to the logical CE. Definstance facts can provide logical support and can receive logical support. In the current implementation, definstance facts can only provide logical support as a whole. In a future version of Jess, it will be possible for a definstance fact to provide logical support based on any combination of individual slot values.

The logical CE can be used together with all the other CEs, including not and exists. A fact can thus be logically dependent on the non-existence of another fact, or on the existence of some category of facts in general.

The Jess language functions dependents and dependencies let you query the logical dependencies among facts.

6.14. The 'forall' conditional element.

The "forall" grouping CE matches if, for every match of the first pattern inside it, all the subsequent patterns match. An example:

(defrule every-employee-has-a-stapler-and-holepunch
  (forall (employee (name ?n))
          (stapler (owner ?n))
          (holepunch (owner ?n)))
   =>
  (printout t "Every employee has a stapler and a holepunch." crlf))

This rule fires if there are a hundred employees and everyone owns the appropriate supplies. If a single employee doesn't own the supplies, the rule won't fire.

6.15. The 'accumulate' conditional element.

The "accumulate" CE is complicated, and perhaps hard to understand, but it's incredibly powerful. It lets you count facts, add up fields, store data into collections, etc. I will eventually need to write quite a bit of documentation for it, but for now, the following should get you started.

The accumulate CE looks like this:

(accumulate <initializer> <action> <result> <conditional element>)

When an accumulate CE is encountered during matching (i.e., when the preceding pattern is matched, or when the contained CE is matched), the following steps occur:

  1. A new execution context is created.
  2. The initializer is executed in that context.
  3. If the CE is activated via the left input, all the matching tokens from the right memory are considered. If it's activated via the right input, each of the matching left tokens are visited. As each is visited, all of its matching right tokens are considered in turn.
  4. For each token considered, the variables it defines are bound in the execution context, and the action is executed.
  5. If a pattern binding is present, the result is bound to the given variable.
  6. Finally, the accumulate CE matches successfully and matching continues at the next conditional element.

What this all means is that "accumulate" lets you execute some code for every match, and returns the accumulated result. For example, this rule counts the number of employees making more than $100,000 per year. A variable is initialized to zero, and incremented for every match; that variable is then bound to the pattern binding.

Jess> (defrule count-highly-paid-employees
  ?c <- (accumulate (bind ?count 0)                        ;; initializer
                (bind ?count (+ ?count 1))                    ;; action
                ?count                                        ;; result
                (employee (salary ?s&:(> ?s 100000)))) ;; CE
  =>
  (printout t ?c " employees make more than $100000/year." crlf))

This variation prints a list of those employees instead by storing all the names in an ArrayList:

Jess> (defrule count-highly-paid-employees
  ?c <- (accumulate (bind ?list (new java.util.ArrayList))   ;; initializer
                (?list add ?name)                             ;; action
                 ?list                                        ;; result
                (employee (name ?name)
                              (salary ?s&:(> ?s 100000))))  ;; CE
  =>
  (printout t (?c toString)  crlf))

Warning: note that because matching one fact can cause accumulate to iterate over a large number of other facts, it can be computationally expensive. Do think about what you're doing when you use it.

6.16. The 'unique' conditional element.

The unique CE has been removed. The parser will accept but ignore it.

6.17. Node index hash value.

The node index hash value is a tunable performance-related parameter that can be set globally or on a per-rule basis. A small value will save memory, possibly at the expense of performance; a larger value will use more memory but lead to faster rule LHS execution.

In general, you might want to declare a large value for a rule that was likely to generate many partial matches (prime numbers are the best choices:)

Jess> (defrule nihv-demo
  (declare (node-index-hash 169))
  (item ?a)
  (item ?b)
  (item ?c)
  (item ?d)
  =>)
See the discussion of the set-node-index-hash function for a full discussion of this value and what it means.

6.18. The 'slot-specific' declaration for deftemplates

Deftemplate definitions can now include a "declare" section just as defrules can. There are several different properties that can be declared. One is "slot-specific". A template with this declaration will be matched in a special way: if a fact, created from such a template, which matches the left-hand-side of a rule is modified, the result depends on whether the modified slot is named in the pattern used to match the fact. As an example, consider the following:


Jess> (deftemplate D (declare (slot-specific TRUE)) (slot A) (slot B))

Jess> (defrule R
  ?d <- (D (A 1))
  =>
  (modify ?d (B 3)))

Without the "slot-specific" declaration, this rule would enter an endless loop, because it modifies a fact matched on the LHS in such a way that the modified fact will still match. With the declaration, it can simply fire once. This behavior is actually what many new users expect as the default; the technical term for it is refraction.

6.19. The 'no-loop' declaration for rules

If a rule includes the declaration (declare (no-loop TRUE)), then nothing that a rule does while firing can cause the immediate reactivation of the same rule; i.e., if a no-loop rule matches a fact, and the rule modifies that same fact such that the fact still matches, the rule will not be put back on the agenda, avoiding an infinite loop. This is basically just a stronger form of "slot-specific."

6.20. Forward and backward chaining

The rules we've seen so far have been forward-chaining rules, which basically means that the rules are treated as if... then statements, with the engine passively executing the RHSs of activated rules. Some rule-based systems, notable Prolog and its derivatives, support backward chaining. In a backwards chaining system, rules are still if... then statements, but the engine seeks steps to activate rules whose preconditions are not met. This behaviour is often called "goal seeking". Jess supports both forward and backward chaining. Note that the explanation of backward chaining in Jess is necessarily simplified here since full explanation requires a good understanding of the underlying algorithms used by Jess.

To use backward chaining in Jess, you must first declare that certain fact templates will be backward chaining reactive. You can do this when you define the template:

Jess> (deftemplate factorial
  (declare (ordered TRUE)
  (backchain-reactive TRUE))
Alternatively, you can use the do-backward-chaining function after the template is defined:

Jess> (do-backward-chaining factorial)
Then you can define rules which match such patterns. Note that templates must be declared to be backwards chaining reactive before you define any rules which use the template.

Jess> (defrule print-factorial-10
  (factorial 10 ?r1)
  =>
  (printout t "The factorial of 10 is " ?r1 crlf))
When the rule compiler sees that a pattern matches a backward chaining reactive template, it rewrites the rule and inserts some special code into the internal representation of the rule's LHS. This code asserts a fact onto the fact-list that looks like
(need-factorial 10 nil)
if, when the rule engine is reset, there are no matches for this pattern. The head of the fact is constructed by taking the head of the reactive pattern and adding the prefix "need-".

Now, you can write rules which match these need-(x) facts.

Jess> (defrule do-factorial
  (need-factorial ?x ?)
  =>
  (bind ?r 1)
  (bind ?n ?x)
  (while (> ?n 1)
    (bind ?r (* ?r ?n))
    (bind ?n (- ?n 1)))
  (assert (factorial ?x ?r)))
The rule compiler rewrites rules like this too: it adds a negated match for the factorial pattern itself to the rule's LHS.

The end result is that you can write rules which match on (factorial), and if they are close to firing except they need a (factorial) fact to do so, any (need-factorial) rules may be activated. If these rules fire, then the needed facts appear, and the (factorial)-matching rules fire. This, then, is backwards chaining! Jess will chain backwards through any number of reactive patterns. For example:

Jess> (do-backward-chaining foo)
TRUE
Jess> (do-backward-chaining bar)
TRUE
Jess> (defrule rule-1
  (foo ?A ?B)
  =>
  (printout t foo crlf))
TRUE
Jess> (defrule create-foo
  (need-foo $?)
  (bar ?X ?Y)
  =>
  (assert (foo A B)))
TRUE
Jess> (defrule create-bar
  (need-bar $?)
  =>
  (assert (bar C D)))
TRUE
Jess> (reset)
TRUE
Jess> (run)
foo
3
        
In this example, none of the rules can be activated at first. Jess sees that rule-1 could be activated if there were an appropriate foo fact, so it generates the request (need-foo nil nil). This matches part of the LHS of rule create-foo cannot fire for want of a bar fact. Jess therefore creates a (need-bar nil nil) request. This matches the LHS of the rule create-bar,which fires and asserts (bar C D). This activates create-foo, which fires, asserts (foo A B), thereby activating rule-1, which then fires.

There is a special conditional element, (explicit), which you can wrap around a pattern to inhibit backwards chaining on an otherwise reactive pattern.

6.21. Defmodules

A typical rule-based system can easily include hundreds of rules, and a large one can contain many thousands. Developing such a complex system can be a difficult task, and preventing such a multitude of rules from interfering with one another can be hard too.

You might hope to mitigate the problem by partitioning a rule base into manageable chunks. Modules let you divide rules and templates into distinct groups. The commands for listing constructs let you specify the name of a module, and can then operate on one module at a time. If you don't explicitly specify a module, these commands (and others) operate by default on the current module. If you don't explicitly define any modules, the current module is always the main module, which is named MAIN. All the constructs you've seen so far have been defined in MAIN, and therefore are often preceded by "MAIN::" when displayed by Jess.

Besides helping you to manage large numbers of rules, modules also provide a control mechanism: the rules in a module will fire only when that module has the focus, and only one module can be in focus at a time.

Note for CLIPS users: Jess's defmodule construct is similar to the CLIPS construct by the same name, but it is not identical. The syntax and the name resolution mechanism are simplified. The focus mechanism is much the same.

6.21.1. Defining constructs in modules

You can define a new module using the defmodule construct:

Jess> (defmodule WORK)
TRUE

You can place a deftemplate, defrule, or deffacts into a specific module by qualifying the name of the construct with the module name:

Jess> (deftemplate WORK::job (slot salary))
TRUE
Jess> (list-deftemplates WORK)
WORK::job
For a total of 1 deftemplates in module WORK.

Once you have defined a module, it becomes the current module:

Jess> (get-current-module)
MAIN
Jess> (defmodule COMMUTE)
TRUE
Jess> (get-current-module)
COMMUTE

If you don't specify a module, all deffacts, templates and rules you define will automatically become part of the current module:

Jess> (deftemplate bus (slot route-number))
TRUE
Jess> (defrule take-the-bus
  ?bus <- (bus (route-number 76))
  (have-correct-change)
  =>
  (get-on ?bus))
TRUE
Jess> (ppdefrule take-the-bus)
"(defrule COMMUTE::take-the-bus
  ?bus <- (COMMUTE::bus (route-number 76))
  (COMMUTE::have-correct-change)
  =>
  (get-on ?bus))"

You can set the current module explicitly using the set-current-module function. Note that the implied template have-correct-change was created in the COMMUTE module, because that's where the rule was defined.

6.21.2. Modules, scope, and name resolution

A module defines a namespace for templates and rules. This means that two different modules can each contain a rule with a given name without conflicting -- i.e., rules named MAIN::initialize and COMMUTE::initialize could be defined simultaneously and coexist in the same program. Similarly, the templates COMPUTER::bus and COMMUTE::bus could both be defined. Given this fact, there is the question of how Jess decides which template the definition of a rule or query is referring to.

When Jess is compiling a rule or deffacts definition, it will look for templates in three places, in order:
  1. If a pattern explicitly names a module, only that module is searched.
  2. If the pattern does not specify a module, then the module in which the rule is defined is searched first.
  3. If the template is not found in the rule's module, the module MAIN is searched last. Note that this makes the MAIN module a sort of global namespace for templates.
The following example illustrates each of these possibilities:

Jess> (assert (MAIN::mortgage-payment 2000))
<Fact-0>
Jess> (defmodule WORK)
TRUE
Jess> (deftemplate job (slot salary))
TRUE
Jess> (defmodule HOME)
TRUE
Jess> (deftemplate hobby (slot name) (slot income))
TRUE
Jess> (defrule WORK::quit-job
  (job (salary ?s))
  (HOME::hobby (income ?i&:(> ?i (/ ?s 2))))
  (mortgage-payment ?m&:(< ?m ?i))
  =>
  (call-boss)
  (quit-job))
TRUE
Jess> (ppdefrule WORK::quit-job)
"(defrule WORK::quit-job
   (WORK::job (salary ?s))
   (HOME::hobby (income ?i&:(> ?i (/ ?s 2))))
   (MAIN::mortgage-payment ?m&:(< ?m ?i))
   =>
   (call-boss)
   (quit-job))"

In this example, three deftemplates are defined in three different modules: MAIN::mortgage-payment, WORK::job, and HOME::hobby. Jess finds the WORK::job template because the rule is defined in the WORK module. It finds the HOME::hobby template because it is explicitly qualified with the module name. And the MAIN::mortgage-payment template is found because the MAIN module is always searched as a last resort if no module name is specified.

Commands which accept the name of a construct as an argument (like ppdefrule, ppdeffacts, etc) will search for the named construct in the same way as is described above.

Note that many of the commands that list constructs (facts, list-deftemplates, rules, etc) accept a module name or "*" as an optional argument. If no argument is specified, these commands operate only on the current module. If a module name is given, they operate on the named module. If "*" is given, they operate on all modules.

6.21.3. Module focus and execution control

In the previous sections I described how modules provide a kind of namespace facility, allowing you to partition a rulebase into manageable chunks. Modules can also be used to control execution. In general, although any Jess rule can be activated at any time, only rules in the focus module will fire. Note that the focus module is independent from the current module discussed above.

Initially, the module MAIN has the focus:

Jess> (defmodule DRIVING)
TRUE
Jess> (defrule get-in-car
  =>
  (printout t "Ready to go!" crlf))
TRUE
Jess> (reset)
TRUE
Jess> (run)
0

In the example above, the rule doesn't fire because the DRIVING module doesn't have the focus. You can move the focus to another module using the focus function (which returns the name of the previous focus module:)

Jess> (focus DRIVING)
MAIN
Jess> (run)
Ready to go!
1

Note that you can call focus from the right-hand-side of a rule to change the focus while the engine is running.

Jess actually maintains a focus stack containing an arbitrary number of modules. The focus module is, by definition, the module on top of the stack. When there are no more activated rules in the focus module, it is "popped" from the stack, and the next module underneath becomes the focus module. You also can manipulate the focus stack with the functions pop-focus list-focus-stack get-focus-stack and clear-focus-stack

The example program dilemma.clp shows a good use of modules for execution control.
6.21.3.1. The auto-focus declaration
You can declare that a rule has the auto-focus property:

Jess> (defmodule PROBLEMS)
TRUE
Jess> (defrule crash
  (declare (auto-focus TRUE))
  (DRIVING::me ?location)
  (DRIVING::other-car ?location)
  =>
  (printout t "Crash!" crlf)
  (halt))
TRUE
Jess> (defrule DRIVING::travel
  ?me <- (me ?location)
  =>
  (printout t ".")
  (retract ?me)
  (assert (me (+ ?location 1))))
TRUE
Jess> (assert (me 1))
<Fact-1>
Jess> (assert (other-car 4))
<Fact-2>
Jess> (focus DRIVING)
MAIN
Jess> (run)
...Crash!
4
                
When an auto-focus rule is activated, the module it appears in is automatically pushed onto the focus stack and becomes the focus module. Modules with auto-focus rules make great "background tasks."
6.21.3.2. Returning from a rule RHS
If the function return is called from a rule's right-hand-side, it immediately terminates the execution of that rule's RHS. Furthermore, the current focus module is popped from the focus stack.

This suggests that you can call a module like a subroutine. You call the module from a rule's RHS using focus and you return from the call using return