Distribution
Category UC-411
SAND98-8206 (revised)
Unlimited Release
First Printed November 1997

Jess, The Java Expert System Shell

http://herzberg.ca.sandia.gov/jess
Ernest J. Friedman-Hill
Distributed Computing Systems
Sandia National Laboratories
Livermore, CA
Version 4.0 (March 23rd, 1998)

ABSTRACT

This report describes Jess, a clone of the popular CLIPS expert system shell written entirely in Java. Jess supports the development of rule-based expert systems which can be tightly coupled to code written in the powerful, portable Java language. The syntax of the Jess language is discussed, and a comprehensive list of supported functions is presented. A guide to calling Java functions from Jess, and to extending Jess by writing Java code, is also included.

1 Introduction

Jess is a clone of the popular expert system shell CLIPS, rewritten entirely in Java. With Jess, you can conveniently give your Java applets and applications the ability to 'reason'. In describing Jess, I am going to describe much of CLIPS itself, but the reader may want to have a copy of the CLIPS manuals available. See the WWW site http://www.ghg.net/clips/CLIPS.html for more information about CLIPS. Note that Jess does not duplicate all of CLIPS, but only the essential core of it.

Jess is compatible with all versions of Java starting with version 1.0.2. It is (in particular) Java 1.1 compatible, although while compiling you will see warnings about deprecated methods. Such is the price of compatibility!

Jess is a work in progress - more features are always being added. The order will be determined in part by what folks seem to want most, what I need Jess to do, and how much time I have to spend on it. See the Section Version History, for a list of what's new in this version of Jess, and see section 1.2 below for a quick overview.

There is a Jess email discussion list you can join. To get information about the jess-users list, send a message to majordomo@sandia.gov containing the text

        help
        info jess-users
        end     
as the BODY of the message.

This is the 4.0 official release. It is enormously faster than Jess 3.2. There may still be bugs. Please report any that you find to me at ejfried@ca.sandia.gov so I can fix them for a later release.

Jess is copyrighted software - see the file LICENSE for details.

1.1 Getting Started With Jess

1.1.1 Unpacking the Distribution
If you download Jess for UNIX, you can extract the files using tar and uncompress:
        uncompress Jess-4.0.tar.Z
        tar xf Jess-4.0.tar
If you downloaded Jess for Windows, you get a .zip file which should be unzipped using a Win32-aware unzip program like WinZip. Don't use PKUNZIP - it cannot handle long file names. WinZip is nice.

When Jess is unpacked, you should have a directory named 'Jess40b4'. Inside this directory should be the following files:

README.html This file
jess/ A directory containing the 'jess' package. There are many source files in here that implement Jess's inference engine; others implement a number of Jess GUIs and command-line interfaces. Main.java implements both the applet interface and the command-line interface. NullDisplay.java is used by the command-line version; QuizDisplay is a very simple GUI console for Jess with both an application and an applet personality.
jess/view Java source implementing the optional Jess 'view' command.
jess/reflect Java source implementing the optional Jess commands that let you create and manipulate Java objects from Jess.
examples/ A directory of tiny example CLIPS files.
jess/examples A directory of example Java files.
index.html A web page containing the Jess example applet; it may need to be edited!
Makefile A simple makefile for Jess.

1.1.2 Compiling Jess

Jess comes as a set of Java source files. You'll need to compile them first. If you have a 'make' utility (any UNIX make, or nmake or GNU make on Win32), you can just run make and the enclosed makefile will build everything. Otherwise the commands

  javac jess/*.java (UNIX)
or
  javac jess\*.java (Win32)
would work just fine, given that you have a Java compiler like Sun's JDK. If you have problems, be sure that the directory in which the jess subdirectory appears is on your CLASSPATH; this may mean including '.' (dot). Don't try to compile from inside the Jess40b4/jess/ directory; it won't work. You can use either a Java 1.0.2 or a Java 1.1 compiler to compile Jess; the resulting code runs on either 1.0 or 1.1 VMs. Note that if you use a 1.1 compiler, you will see some warning about 'deprecated methods' - it is safe to ignore these warnings. I could make them go away, but then Jess would not be 1.0.2 compatible!

There are a number of optional source files in the subdirectories Jess40b4/jess/view/, Jess40b4/jess/reflect/ and Jess40b4/jess/examples/* that aren't compiled if you follow the instructions above. These files define the optional debugging command view, the 'reflection' commands new, call, set, get, set-member, and get-member, and the Java object matching commands defclass and definstance. They can be compiled only with Java 1.1 or later. If you have such a compiler, then you can issue a command like

   javac jess/*.java jess/view/*.java jess/reflect/*.java jess/examples/*/*.java
to compile everything.
1.1.3 Jess Example Programs

There are several example programs for you to try. They are called fullmab.clp, zebra.clp, and wordgame.clp. fullmab.clp is the Monkey and Bananas problem featured at the Jess web site. To run it yourself from the command line, just type

     java jess.Main examples/fullmab.clp (or examples\fullmab.clp on Win32)
and the problem should run, producing a few screensfull of output. Any file of CLIPS code (given that it contains only CLIPS constructs and functions implemented by Jess, as described in this document) can be run this way. Note that giving Jess a file name on the command line is like using the 'batch' command in CLIPS; therefore, you need to make sure that the file ends with
     (reset)  
     (run)
or nothing will happen. zebra.clp and wordgame.clp are two other classic CLIPS examples, slightly modified to run under Jess. Both of these examples were selected to show how Jess deals with tough situations. These examples both generate huge numbers of partial pattern matches, so they are slow and use up a lot of memory. They each take about one second to run, depending on your computer.
1.1.4 Command-line Interface

Jess also has an interactive command-line interface, which has been improved for Jess 4.0. Just type java jess.Main to get a 'Jess>' prompt. In support of this, there is now an (exit) command. To execute a file of CLIPS code from the command prompt, use the 'batch' command:

     Jess> (batch myfile.clp)
       (lots of output)
Jess now sports a 'system' command, which means, for example, that you can invoke an editor from the Jess command line to edit a file of Jess code before reading it in with 'batch'. 'system' will also help to allow non-Java programmers to integrate Jess with other applications. Given that you have an editor named 'notepad' on your system, try
      Jess> (system notepad README)
        TRUE
1.1.5 Jess as an Applet
The class 'jess.Main', which contains the main routine that allows you to execute CLIPS code from the command line, also implements an Applet interface, so that it will run in a Web browser. The Applet interface is specialized to run only the 'mab.clp' Monkey and Banana example. To create your own graphical applets using the Jess classes, read on, and check out the Sections about calling Jess from Java and vice-versa. You can modify the jess.Main class, or you can write your own from scratch (which is probably a better idea.) Note, in particular, that the applet version of the jess.Main class does not load in all the optional functions; see the description of the addUserfunction Java method and the (load-function) Jess method for info about doing this.

1.1.6 Where To Go Next
If you are completely new to CLIPS, you should skip ahead to Chapter 2 of this manual. If you are an experienced CLIPS programmer, you can browse through Chapter 2 next, paying attention to differences between Jess and CLIPS. If you've used Jess before, then read the list of new features in the next section.

1.2 What's New in This Release

If you've used Jess before, you'll notice many changes in version 4.0.


2 The Jess Language

Jess is effectively an interpreter for a rule language borrowed from CLIPS. Given CLIPS' heritage (it was strongly influenced by systems written in LISP) this rule language is basically a small, idiosyncratic version of LISP, making Jess a LISP interpreter written in Java. I will briefly describe this language here; more information can be gotten from the CLIPS manuals themselves.

I'm using an extremely informal notation to describe syntax. Basically strings in <angle-brackets> are some kind of data that must be supplied; things in [square brackets] are optional, and ellipses (...) are used to indicate one or more of the preceding. In general, input to Jess is free-format; newlines are generally not significant and are treated as whitespace.

In the example dialogs, you type what appears after the Jess> prompt. The system responds with the text in bold.

2.1 Atoms

An 'atom', or symbol, is a common concept in the Jess language. Atoms are very much like identifiers in other languages. A Jess atom can contain letters, numbers, and the following punctuation: $*=+/<>_?#. . An atom may not begin with a number; it may begin with some punctuation marks (some have special meanings as operators when they appear at the start of an atom.) The best atoms consist of letters, numbers, underscores, and dashes; dashes are traditional word separators. The following are all valid atoms:
  foo   first-value   contestant#1   _abc

2.2 Numbers

Jess parses numbers using the Java StreamTokenizer class. Therefore, it accepts only simple floating point and integer numbers; it does not accept scientific or engineering notation. The following are all valid numbers:
  3   4.  5.643

2.3 Strings

Character strings in Jess are denoted using "double quotes." Backslashes can be used to escape embedded quote symbols. The following are all valid strings:
  "foo"     "Hello, World"     "\"Nonsense\," he said firmly."

2.4 Lists

The fundamental unit of syntax in Jess is the list. A list always consists of an enclosing set of parentheses and zero or more atoms, numbers, strings, or other lists. The following are valid lists:
  (+ 3 2)  (a b c)  ("Hello, World") ()  (deftemplate foo (slot bar))
The first element of a list (the 'car' of the list in LISP parlance) is often called the list's 'head' in Jess.

2.5 Comments

Programmer's comments in Jess begin with a semicolon (;) and extend to the end of the line of text. Comments cannot appear inside of constructs (see below, Constructs). Here is an example of a comment
  ; This is a list
  (a b c)

2.6 Functions

Jess contains a large number of built-in functions that you may call; more functions are provided as extensions. You can write your own functions in the Jess language (see below, Deffunctions) or in Java (see Extending Jess with Java. )

Function calls in Jess use a prefix notation. A list whose head is an atom that is the name of an existing function can be evaluated as an expression. For example, an expression that uses the "+" function to add the numbers 2 and 3 would be written (+ 2 3). When evaluated, the value of this expression is the number 5 (not a list containing the single element 5!) In general, expressions are recognized as such and evaluated in context when appropriate. You can type expressions at the Jess> prompt; Jess evaluates the expression and prints the result.

  Jess> (+ 2 3)
    5
  Jess> (+ (+ 2 3) (* 3 3))
    14
Note that arithmetic results may be returned as floating-point numbers or as integers, depending on the types of the arguments.

Jess implements only a small subset of CLIPS intrinsic functions. These are functions which are essentially built into Jess and cannot be removed. All of these have been designed to function as much like their CLIPS counterparts as possible. The currently supported intrinsic functions are

        *, +, -, /, <, <=, <>, =, >, >=, and, assert, assert-string,
        bind, clear, close, eq, eq*, exit, facts, foreach, gensym*, get-var,
        halt, if, jess-version-number, jess-version-string, load-facts,
        mod, modify, neq, not, open, or, printout, read, readline, reset,
        retract, return, rules, run, save-facts, sym-cat, undefrule,
        unwatch, watch, while
On the other hand, I'm supplying implementations for many more CLIPS functions as 'Userfunctions' - external functions written in Java that you can plug into Jess. See the files jess/StringFunctions.java (string handling functions: str-cat, str-compare, etc), jess/MultiFunctions.java (multifield functions: create$, nth$), jess/PredFunctions.java (predicates: oddp, stringp, etc), jess/MiscFunctions.java (batch, system), and jess/MathFunctions.java (abs, sqrt) for more information. All of the included Userfunctions are installed into the command-line version of Jess by default; you can pick and choose in your own applications. In applets, in particular, you may want to include only the Userfunctions you need, to keep the size of the applet down. (see Extending Jess with Java for information about doing this.)

Here is the complete list of Userfunctions shipped with Jess 4.0:

        **, abs, bag, batch, build, call, complement$, create$, delete$,
        definstance, defclass, div, e, engine, eval, evenp, exp, explode$, first$, float, floatp,
        format, get, get-member, implode$, insert$, integer, integerp, intersection$,
        length$, lexemep, list-function$, load-function, load-package,
        log, log10, lowcase, max, member$, min, multifieldp, new, nth$,
        numberp, oddp, pi, ppdefrule, random, replace$, rest$, round,
        set, set-member, setgen, socket, sqrt, str-cat, str-compare, str-index,
        str-length, stringp, sub-string, subseq$, subsetp, symbolp,
        system, time, undefinstance, union$, upcase  
All these functions are described in detail later in this document.

2.7 Variables

Programming variables in Jess are atoms that begin with the question mark (?) character. The question mark is part of the variable's name. A normal variable can refer to a single atom, number, or string; a variable whose first character is instead a "$" (for example, $?X) is a multivariable, which can refer to a special kind of list called multifield. You assign to any variable using the bind function:
  (bind ?x "The value")
Multifields are generally created using special multifield functions like <#createmf>create$, and can then be boud to multivariables:
  (bind $?grocery-list (create$ eggs bread milk))
Variables need not (and cannot) be declared before their first use (except for Defglobals.)

2.8 Constructs

Besides expressions and multifields, the Jess language includes another kind of special list called a 'construct.' A construct is a list that defines something to the Jess system itself. For example, the deffunction construct is used to define functions (see below, 2.9, Deffunctions.) A construct evaluates to TRUE if it was accepted, or FALSE if it was not.

2.9 Deffunctions

The deffunction construct is used to define functions that you can then call from Jess. A deffunction construct looks like this:
(deffunction <function-name>   [<doc-comment>] ([<parameter1> [<parameter2> [...]]])
  [<expr1> [<expr2> [...]]]
  [<return-specifier>])
The <function-name> must be an atom. Each parameter must be a variable name (all functions use pass-by-value semantics). The optional <doc-comment> is a double-quoted string that can describe the purpose of the function. The <expr> are an arbitrary number of arbitrary expressions. The optional <return-specifier> gives the return value of the function. It can either be an explicit use of the 'return' function, or it can be any value or expression. Control flow in deffunctions is achieved via the special control-flow expressions 'while' and 'if'. The following is a deffunction that returns the numerically larger of its two arguments:
(deffunction max (?a ?b)
  (if (> ?a ?b) then
      (return ?a)
   else
      (return ?b)))

2.10 Facts

Jess maintains a list of "facts", or information about the current state of the system. Facts come in two categories: ordered and unordered. Ordered facts are merely lists whose head must be an atom:
        (temperature 98.6)
        (shopping-list bread milk paper-towels)
        (start-processing)
Unordered facts are more structured; they contain a definite set of 'slots' which must be accessed by name. While ordered facts can be used without prior definition, unordered facts must be defined using the deftemplate construct (see below, Deftemplates ).

Facts are placed on the fact-list by the 'assert' function. You can see the current fact list using the 'facts' function. You can remove a fact from the fact-list if you know its 'fact ID'. For example,

  Jess> (assert (foo bar))
    <Fact-0>
 
  Jess> (facts) 
    f-0   (foo bar)
    For a total of 1 facts.
    TRUE
  Jess> (retract 0)
    TRUE
  Jess> (facts)
    For a total of 0 facts.
    TRUE

2.11 Deftemplates

To define a type of unordered fact, use the deftemplate construct:
(deftemplate <deftemplate-name>
  [<doc-comment>]
  (slot <slot-name> [(default <value>)] [(type <typespec>)])
  [(slot ...) ...])
The <deftemplate-name> is the head of the facts that will be created using this deftemplate. The <slot-name> must be an atom. The 'default' slot qualifier states that the default value of a slot in a new fact is given by <value>; the default is the atom 'nil'. The 'type' slot qualifier is accepted (for CLIPS compatibility) but ignored by Jess.

As an example, defining the following deftemplate

  (deftemplate automobile
        "A specific car."
        (slot make)
        (slot model)
        (slot year)
        (slot color (default white)))
would allow you to define facts like this:
  Jess> (assert (automobile (make Chrysler) (model LeBaron) (year 1997)))
    <Fact-0>
  Jess> (facts)    
    f-0   (automobile (make Chrysler) (model LeBaron) (year 1997) (color white))
    For a total of 1 facts.
    TRUE
Note that the car is white, by default. Also note that any number of additional automobiles could also be simultaneously asserted onto the fact list using this deftemplate.

A given slot in a deftemplate fact can normally hold only one value. If you want a slot that can hold multiple values, use the 'multislot' keyword instead:

  (deftemplate box
     (slot location)
     (multislot contents))

  (assert (box (location kitchen) (contents spatula sponge frying-pan)))

2.12 Defclasses

A defclass construct is used to tell Jess that you would like to be able to use a specific Java class like a deftemplate, and instances of that class like facts. The syntax of defclass is simple:
  (defclass pump jess.examples.pumps.Pump)
The tag pump is just like a deftemplate name. Jess automatically builds a deftemplate from the named Java class, in this case jess.examples.pumps.Pump , an example class shipped with Jess. Jess turns Java Beans properties into deftemplate slots (if you're not familiar with Java Beans, this basically means that Jess will find any methods in the class named get<X> or is<X> which return non-void and take no arguments. Jess uses the BeanInfo mechanism to learn about Bean properties.) As an example, the jess.examples.pumps.Pump class looks something like this (lots cut out!):
public class Pump
{
  public String getName() { ... }
  public int getFlow() { ... }
  public void setFlow(int f) { ... }
}

Jess will generate this deftemplate
  (deftemplate pump "$JAVA-OBJECT$ jess.examples.pumps.Pump"
        (slot class)
        (slot name)
        (slot flow)
        (slot OBJECT))
The extra slot class comes from the getClass() method inherited from java.lang.Object; and the OBJECT slot is added to every defclass by Jess: it always holds a reference to the Java object the matched pattern refers to. We're getting a bit ahead of ourselves; we'll come back to object matching below when we talk about the definstance construct.

There's a simple Bean class to experiment with in jess.examples.simple.

2.13 Deffacts

The deffacts construct is a handy way to define a list of facts that should be made true when the Jess system is started or reset.
  (deffacts <deffacts-name>
    [<doc-comment>]   
    <fact1>
    [...])
The deffacts-name is not used; its primary purpose is documentation. A deffacts can contain any number of facts. Any unordered facts in a deffacts must have previously been defined via a deftemplate construct when the deffacts is parsed. The following is a valid deffacts construct:
  (deffacts automobiles
    (automobile (make Chrysler) (model LeBaron) (year 1997))
    (automobile (make Ford) (model Contour) (year 1996))
    (automobile (make Nash) (model Rambler) (year 1948)))

2.14 Definstances

While a deffacts construct defines an initial set of facts to the Rete engine, the definstance construct tells Jess that one particular Java object should be treated as if it were a fact and be matched by deftemplate patterns defined in defclass constructs. An object to be treated in this way must satisfy two requirements:
  1. It must be assignable to a reference of the type defined in a previous defclass; in other words it must be of the same class, a subclass, or an implementor of a defclassed interface.
  2. It must support PropertyChangeListeners (an event mechanism used by Java Beans); i.e., it must support the addPropertyChangeListener(PropertyChangeListener pcl) method. Note that none of the classes in the Java API support PropertyChangeListeners; however, many Java Beans do, and support is easily added to other classes using the java.beans.PropertyChangeSupport class.

In any case, you can install an object for pattern matching like this

  (definstance pump (new jess.examples.pumps.Pump "MAIN" ?tank))
where the first argument to definstance is a defclass tag, and the second is an expression returning the Java object. Here we are creating the object using the new function (see below for a discussion of how to do this), but it instead could come from a variable, from the return value of a Userfunction, etc. "MAIN" and ?tank are arguments to the jess.examples.pumps.Pump constructor.

Once declared in a definstance construct, a Java object will be represented at all times by a single fact in Jess. This fact will be modified each time the object sends a PropertyChangeEvent. You can write patterns on the LHS of a rule to match this automatically generated and maintained fact, and as a result, you can match the state of the Java object. For example, given the pump defclass and definstance, we can write the following rule:

  (defrule decrease-pump-flow-if-high-1
    "If a pump's flow rate is over 20, decrease it by 5 units."
    (pump (flow ?f&:(> ?f 20)) (OBJECT ?pump))
    =>
    (set ?pump flow (- ?f 5)))
Note that by binding the value of the OBJECT slot on the LHS, we can modify the matched object from the RHS of the rule. You can also (modify) function for this purpose:
  (defrule decrease-pump-flow-if-high-2
    "If a pump's flow rate is over 20, decrease it by 5 units."
    ?pump <- (pump (flow ?f&:(> ?f 20)))
    =>
    (modify ?pump (flow (- ?f 5))))
These two rules are equivalent; the latter may be slightly more efficient.

The Pump example given here is taken from a full, working example that ships with Jess. To try it out, compile the classes in jess/examples/pumps:

  javac jess\examples\pumps\*.java
and then run the example
  java jess.Main examples\pumps\pumps.clp
Read the Java source files and the pumps.clp file to see what's going on. It's a real-time control problem, and Jess does a passable job of keeping two leaky tanks from overflowing or running dry. The Pumps and Tanks are Java objects that run in their own Threads, while Jess reacts to their PropertyChangeEvents, triggering rules which in turn adjust the Pumps.

Note: You must be careful not to trigger any PropertyChangeEvents via calculations you perform on the LHS of any rule. A rule of thumb is not to set any Java variables or call any methods that might possible change an objects state; property accessor methods are fine. Violation of this warning can cause Thread deadlock in the engine.

2.15 Defrules

The main purpose of a shell like Jess is to support the execution of rules. Rules in Jess are somewhat like the IF...THEN statements of other programming languages; in operation, Jess constantly tests to see if any of the IFs become true, and executes the corresponding THENs (actually, it doesn't work quite this way, but this is a good way to imagine things. See below, How Jess Works, for a more truthful explanation.) The 'intelligence' embedded in an intelligent rule-based system is encoded in the rules. The defrule construct is used to define a rule to Jess.
  (defrule <defrule-name>
    [<doc-comment>]
    [<salience-declaration>]
    [[<pattern-binding> <- ] <pattern1>]
    [ (more patterns) ]
    =>
    [<action1> [ <action2> ... ]])
Basically, a rule consists of a list of patterns (the IF part, on the rule's left-hand-side (LHS)) and a list of actions (the THEN part, on the rule's right-hand-side, or RHS). The patterns are matched against the fact list. When facts are found that match all the patterns of a rule, the rule becomes activated, meaning it may be fired (have its actions executed). An activated rule may become deactivated before firing if the facts that matched its patterns are retracted, or removed from the fact list, while it is waiting to be fired. Here is an example of a simple rule:
  (defrule example-1
    "Announce 'a b c' facts"
    (a b c)
   =>
    (printout t "Saw 'a b c'!" crlf))
To see this rule in action, enter it at the Jess> prompt, assert the fact (a b c), then the (run) command to start the Jess engine. You'll get some interesting additional information by first issuing the (watch all) command:
  Jess> (clear)
    TRUE
  Jess> (watch all)
    TRUE
  Jess> (defrule example-1
            "Announce 'a b c' facts"
            (a b c)
           =>
            (printout t "Saw 'a b c'!" crlf))
    example-1: +1+1+1+1+t
    TRUE
  Jess> (assert (a b c))
     ==> Activation: example-1 : f-0
     ==> (a b c)
     <Fact-0>
  Jess>  (run)
    FIRE [Defrule: example-1 "Announce 'a b c' facts";
                   1 patterns; salience: 0] f-0
    Saw 'a b c'!
    TRUE
  Jess>
When you enter the rule, you see the sequence of symbols +1+1+1+1+t. This tells you something about the way that Jess compiled the rule you wrote into the internal rule representation. Then when you assert the fact, Jess responds by telling you that the new fact was assigned the numeric fact identifier 0 (f-0), and that it is an ordered fact with head 'a' and additional fields 'b' and 'c'. Then it tells you that the rule example-1 is activated by the fact f-0, that fact you just entered. When you type (run), you see an indication that your rule has been fired, including a list of the relevant fact IDs. The line "Saw 'a b c'!" is the result the execution of your rule.

Multiple activated rules are fired in order of salience (see below). Within a given salience value, the most recently activated rules will fire first (CLIPS' depth strategy, the default.)

If all the patterns of a rule had to be given literally as above, Jess would not be very powerful. Patterns can, however, also include wildcards and various kinds of predicates (comparisons and boolean functions). Firstly, you can specify a variable name instead of a value 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

  (defrule example-2
    (a ?x ?y)
    =>
    (printout t "Saw 'a " ?x " " ?y "'" crlf))
will be activated each time any fact with head 'a' having two fields is asserted: (a b c), (a 1 2), (a a a), etc. As in the example, the variables thus matched in the patterns (or left-hand-side, LHS) of a rule are available in the actions (right-hand-side, RHS) of the same rule.

Each such variable field in a pattern 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 ampersands. (The variable name itself is actually optional). Tests can be:

Here's an example of a rule that uses several kinds of tests.
  (defrule example-3
    (not-b-and-c ?n1&~b ?n2&~c) 
    (different ?d1 ?d2&~?d1)
    (same ?s ?s)
    (more-than-one-hundred ?m&:(> ?m 100))
    =>
    (printout t "Found what I wanted!" crlf))
The first pattern will match a 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 last pattern matches a fact with head 'more-than-one-hundred' and a single field with a numeric value greater than 100.

A few more details about patterns: you can match a field without binding it to a variable by omitting the variable name and using just a question mark (?) as a placeholder. You can match any number of fields using a multivariable (one starting with $?):

  Jess> (defrule example-4
    (grocery-list $?list)
    =>
    (printout t "I need to buy " $?list crlf))
    TRUE
  Jess> (assert (grocery-list eggs milk bacon))
    TRUE
  Jess> (run)
    I need to buy eggs milk bacon
    TRUE
2.15.1 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:
  (defrule example-5
    ?fact <- (command "retract me")
    =>
    (retract ?fact))
The variable (?fact, in this case) is assigned the fact ID of the particular fact that activated the rule.
2.15.2 Salience.
Rules normally fire in a somewhat unpredictable order; the most recently activated rules will fire first. To influence this order, rules can include a salience declaration:
  (defrule example-6
    (declare (salience -100))
    (command exit-when-idle)
    =>
    (printout t "exiting..." crlf))
(This rule contains no patterns). 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.
2.15.3 'Not' patterns.
A pattern can be enclosed in a list with 'not' as the head. In this case, the pattern is considered to match if a fact which matches the pattern is not found. For example:
  (defrule example-7
     (person ?x)
     (not (married ?x))
     =>
     (printout t ?x " is not married!" crlf))
Note that a 'not' pattern cannot contain any variables that are not bound before that pattern (since a 'not' pattern does not match any facts, it cannot be used to define the values of any variables!) You can use blank variables, however (a blank variable is a bare '?' or '$?'.) A 'not' pattern can similarly not have a pattern binding.
2.15.4 The 'Test' conditional element.
A pattern with 'test' as the head is special; the body consists not of slot tests but of a single function which is evaluated and whose truth determines whether the pattern matches. For example
  (defrule example-8
     (person (age ?x))
     (test (> ?x 30))
     =>
     (printout t ?x " is over 30!" 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))

2.16 Defglobals

Jess can support 'global variables' that are visible from the command-prompt or inside any rule or deffunction. You can define them using the defglobal construct:
        (defglobal
         <varname1> = <value1>
         [<varname2> = <value2> [...]])
Note that defglobals are reset to their assigned values by the (reset) command. Unlike CLIPS, the initialization value is evaluated once, at compile-time, not each time a (reset) is issued.

2.17 Things Not Implemented In Jess

Jess does not implement all features of all CLIPS constructs. This list tries to explain some of what's missing from Jess to those who know CLIPS. If you're not already a CLIPS user, you should skip this section!
2.17.1 Defrules
2.17.2 Deffunctions
2.17.3 Deftemplates
The only supported slot attribute in Jess is the 'default' attribute. In particular, 'type' will parse, but is ignored at runtime.
2.17.4 COOL, FuzzyCLIPS, wxCLIPS, etc
Jess does not implement any features of these CLIPS extensions. Note that defclass and definstance are keywords in CLIPS that form part of COOL; although these keywords exist in Jess, their syntax and precise meaning is different.
2.17.5 Modules
Jess does not implement CLIPS modules. However, since Jess itself is object-oriented, you can instantiate multiple Jess systems and get them to communicate via the external function interface.

3 Jess Function Guide

In this Section, every Jess language function shipped with Jess version 4.0 is described. Some of these functions are intrinsic functions, while some are Userfunctions, and may not be available to all Jess code, as detailed above. All of these functions are installed into the command-line verison of Jess; to use a function not marked "built-in" in your own programs, you need to add the appropriate Userpackage using Rete.addUserpackage(new <pkgname>()). The package for each function is listed below.

*

Package:
(built-in)
Arguments:
One or more numeric arguments
Returns:
Number
Description:
Multiplies any number of numeric arguments and returns their product.The return value is an INTEGER unless any of the arguments are FLOAT, in which case it is a FLOAT.

**

Package:
jess.MathFunctions
Arguments:
Two numbers
Returns:
Number
Description:
Raises the first argument to the power of the second using Java's Math.pow() function.

+

Package:
(built-in)
Arguments:
One or more numeric arguments
Returns:
Number
Description:
Adds any number of numeric arguments and returns their sum. The return value is an INTEGER unless any of the arguments are FLOAT, in which case it is a FLOAT.

-

Package:
(built-in)
Arguments:
One or more numeric arguments
Returns:
Number
Description:
Subtracts the second and later arguments from the first, and returns the difference. The return value is an INTEGER unless any of the arguments are FLOAT, in which case it is a FLOAT.

/

Package:
(built-in)
Arguments:
One or more numeric arguments
Returns:
Number
Description:
Multiplies all but the first argument together, then divides this product into the first argument; returns the quotient as a FLOAT

<

Package:
(built-in)
Arguments:
Two or more numeric arguments
Returns:
Boolean
Description:
Returns TRUE if the first argument is less than the second and all later arguments.

<=

Package:
(built-in)
Arguments:
Two or more numeric arguments
Returns:
Boolean
Description:
Returns TRUE if the first argument is less than or equal to the second and all later arguments.

<>

Package:
(built-in)
Arguments:
Two or more numeric arguments
Returns:
Boolean
Description:
Returns TRUE if the first argument is not equal to any of the second and all later arguments.

=

Package:
(built-in)
Arguments:
Two or more numeric arguments
Returns:
Boolean
Description:
Returns TRUE if the first argument is equal to all of the second and later arguments. The integer 2 and the float 2.0 are =, but not eq.

>

Package:
(built-in)
Arguments:
Two or more numeric arguments
Returns:
Boolean
Description:
Returns TRUE if the first argument is greater than the second and all later arguments.

>=

Package:
(built-in)
Arguments:
Two or more numeric arguments
Returns:
Boolean
Description:
Returns TRUE if the first argument is greater than or equal to the second and all later arguments.

abs

Package:
jess.MathFunctions
Arguments:
One number
Returns:
Number
Description:
Returns the absolute value of the argument.

and

Package:
(built-in)
Arguments:
One or more boolean expressions
Returns:
Boolean
Description:
Returns TRUE only if all arguments evaluate to TRUE.

assert

Package:
(built-in)
Arguments:
One or more facts (not fact-IDs.)
Returns:
Fact-ID or FALSE
Description:
Asserts all facts onto the fact-list; returns fact-ID of last fact asserted, or FALSE if no facts were successfully asserted (for example, if all facts given are duplicates of existing facts.)

assert-string

Package:
(built-in)
Arguments:
One string, containing a representation of a fact.
Returns:
Fact-ID or FALSE
Description:
Attempts to parse string as a fact, and if successful, returns the value returned by assert with the same fact. Note that the string must contain the fact's enclosing parentheses.

bag

Package:
jess.BagFunctions
Arguments:
An atom (a subcommand) and a variable number of additional arguments; see below.
Returns:
(Varies)
Description:
The bag command lets you manipulate Java Hashtables from Jess. The net result is that you can create any number of associative arrays or property lists; each such array or list has a name by which it can be looked up. The lists can contain other lists as properties, or any other Jess data type.

The bag command does different things based on its first argument. It's really seven commands in one:

batch

Package:
jess.Miscfunctions
Arguments:
One string or atom, the name of a file
Returns:
(Varies)
Description:
Attempts to parse and evaluate the given file as Jess code. If successful, returns the return value of the last expression in the file.

bind

Package:
(built-in)
Arguments:
Two, a variable name and any value
Returns:
(Varies)
Description:
Assigns the given value to the given variable, creating the variable if necessary. Note that (as in CLIPS) this works best in rules and deffunctions, and not from the command prompt. Returns the given value.

build

Package:
jess.Miscfunctions
Arguments:
One string, some Jess code
Returns:
(Varies)
Description:
Attempts to parse and evaluate the given string as Jess code. If successful, returns the return value of the last expression in the string. This is typically used to define rules from Jess code; i.e., (build "(defrule foo (foo) => (bar))")

call

Package:
jess.reflect.ReflectFunctions
Arguments:
Two or more: an external address or String, a String, and any number of additional arguments.
Returns:
(Varies)
Description:
Calls a Java method on the given object, or a static method of the class named by the first argument. The second argument is the name of the method, and subsequent arguments are passed to the method. Arguments are promoted and overloaded methods selected precisely as for new. The return value is converted to a suitable Jess value before being returned.

The functor 'call' may be omitted if the method being called is non-static and the object is represented by a simple variable. The following two method calls are equivalent:

        ;; These are legal and equivalent
        (call ?vector addElement (new java.lang.String "Foo"))
        (?vector addElement (new java.lang.String "Foo"))
'Call' may not be omitted if the object comes from the return value of another function call:
        ;; This is illegal
        ((new java.lang.Vector 10) addElement (new java.lang.String "Foo"))

clear

Package:
(built-in)
Arguments:
None
Returns:
TRUE
Description:
Clears Jess. Deletes all rules, deffacts, defglobals, deftemplates, facts, activations, etc. Userfunctions are not deleted.

close

Package:
(built-in)
Arguments:
One or more router identifiers (atoms)
Returns:
TRUE
Description:
Closes any I/O routers associated with the given name by calling close() on the underlying stream, then removes the routers. Any subsequent attempt to use a closed router will report 'bad router.' See open.

complement$

Package:
jess.MultiFunctions
Arguments:
Two multifields
Returns:
Multifield
Description:
Returns a multifield consisting of all elements of argument 2 not appearing in argument 1.

create$

Package:
jess.MultiFunctions
Arguments:
Any number of arbitrary values
Returns:
Multifield
Description:
Returns a new multifield containing all the given arguments. Note that multifields must be created explicitly using this function or others that return them; they cannot be directly parsed from Jess input.

delete$

Package:
jess.MultiFunctions
Arguments:
A multifield and two numbers
Returns:
Multifield
Description:
Creates a new multifield by removing elements from the given multifield. The first numeric argument is the one-based index of the first element to remove; the second is the one-based index of the last element to remove.

div

Package:
jess.MathFunctions
Arguments:
Two numbers
Returns:
Numbers
Description:
Quotient of two numbers, properly rounded to the nearest integer.

e

Package:
jess.MathFunctions
Arguments:
None
Returns:
Number
Description:
Returns the transcendental number 'e'.

engine

Package:
jess.MiscFunctions
Arguments:
None
Returns:
External address
Description:
Returns an external-address object containing the Rete engine in which the function in called.

eq

Package:
(built-in)
Arguments:
Any number of arbitrary arguments
Returns:
Boolean
Description:
Returns TRUE if the first argument is 'equivalent' to all the others. For strings, this means identical contents. Uses the Java Object.equals() function, so can be redefined for external types. Note that the integer 2 and the floating-point number 2.0 are not eq, but they are eq* and =.

eq*

Package:
(built-in)
Arguments:
Any number of arbitrary arguments
Returns:
Boolean
Description:
Returns TRUE if the first argument is 'equivalent' to all the others. Uses numeric equality for numeric types, unlike eq. Note that the integer 2 and the floating-point number 2.0 are not eq, but they are = and eq*.

eval

Package:
(built-in)
jess.MiscFunctions
One string containing a valid Jess expression
Returns:
Varies
Description:
Evaluates the string as if entered at the command line, and returns the result.

evenp

Package:
jess.PredFunctions
One integer
Returns:
Boolean
Description:
TRUE if number is an even integer. Results with non-integers may be unpredictable.

exit

Package:
(built-in)
Arguments:
None
Returns:
Nothing
Description:
Exits Jess and halts Java.

exp

Package:
jess.MathFunctions
Arguments:
One number
Returns:
Number
Description:
Returns 'e' raised to the power of the given argument.

explode$

Package:
jess.MultiFunctions
Arguments:
One string
Returns:
Multifield
Description:
Parses the string as if by a succession of 'read' calls, then returns these individual values as the elements of a multifield.

facts

Package:
(built-in)
Arguments:
None
Returns:
TRUE
Description:
Prints a list of all facts on the fact-list.

first$

Package:
jess.MultiFunctions
Arguments:
One multifield
Returns:
Mutifield
Description:
Returns the first element of the given multifield as a new one-element multifield.

float

Package:
jess.MathFunctions
Arguments:
One number
Returns:
Floating-point number
Description:
Returns the given argument as a float.

floatp

Package:
jess.PredFunctions
Arguments:
One number
Returns:
Boolean
Description:
Returns TRUE if the given number has a non-zero fractional component.

foreach

Package:
(built-in)
Arguments:
A variable, a multifield expression, and any number of additional arguments
Returns:
Varies
Description:
The named variable is set to each of the values in the multifield in turn; for each value, all of the other arguments are evaluated in order. The return function can be used to break the iteration.

format

Package:
jess.MiscFunctions
Arguments:
A router identifier, a format string, and any number of additional arguments
Returns:
A string
Description:
Formats the arguments into a string according to the format string, which is identical to that used by printf in the C language (find a C book for more info!) Returns the string, and optionally prints the string to the named router. If you pass 'nil' for the router name, no printing is done.

get

Package:
jess.reflect.ReflectFunctions
Arguments:
An external address and a string.
Returns:
(Varies)
Description:
Retrieves the value of a Java Bean's property. The first argument is the object and the second argument is the name of the property. The return value is converted to a suitable Jess value exactly as for call.

get-member

Package:
jess.reflect,ReflectFunctions
Arguments:
An external address or a string, and a string.
Returns:
(Varies)
Description:
Retrieves the value of a Java object's data member. The first argument is the object (or the name of a class, for a static member) and the second argument is the name of the field. The return value is converted to a suitable Jess value exactly as for call.

gensym*

Package:
(built-in)
Arguments:
None
Returns:
Atom
Description:
Returns a unique atom. The atom will consist of the letters "gen" plus an integer. You can set the value of this integer to be used by the next gensym call using setgen (see below.)

get-var

Package:
(built-in)
Arguments:
A string or atom
Returns:
(Varies)
Description:
Fetches the value of a variable, given the name of the variable as a string or atom. Rarely needed, but when you need it, you'll know.

halt

Package:
(built-in)
Arguments:
None
Returns:
TRUE
Description:
Halts rule execution. No effect unless called from the RHS of a rule.

if

Package:
(built-in)
Arguments:
A Boolean variable or function call returning Boolean, the atom 'then', and any number of additional expressions; optionally followed by the atom 'else' another list of expression.
Returns:
(Varies)
Description:
The boolean expression is evaluated. If it does not evaluate to FALSE, the first list of expressions is evaluated, and the return value is that returned by the last expression. If it does evaluate to FALSE, and the optional second list of expressions is supplied, those expressions are evaluated and the value of the last is returned.
Example:
(if (> ?x 100)
        then
          (printout t "X is big" crlf)
        else
          (printout t "X is small" crlf))

implode$

Package:
jess.MultiFunctions
Arguments:
One multifield
Returns:
String
Description:
Converts each element of the multifield to a string, and returns these strings catenated together with single intervening spaces.

insert$

Package:
jess.MultiFunctions
Arguments:
A multifield, an integer, and another multifield
Returns:
A multifield
Description:
Inserts the elements of the second multifield so that they appear starting at the given index of the first multifield.

integer

Package:
jess.MathFunctions
Arguments:
One number
Returns:
Integer
Description:
Truncates any fractional component of the given number and returns the integral part.

integerp

Package:
jess.PredFunctions
Arguments:
One number
Returns:
Boolean
Description:
Returns TRUE if the given number has no fractional component.

intersection$

Package:
jess.MultiFunctions
Arguments:
Two multifields
Returns:
Multifield
Description:
Returns a multifield consisting of the elements the two argument multifields have in common.

jess-version-number

Package:
(built-in)
Arguments:
None
Returns:
Float
Description:
Returns a version number for Jess; currently 4.0 .

jess-version-string

Package:
(built-in)
Arguments:
None
Returns:
String
Description:
Returns a human-readable string descriptive of this version of Jess.

length$

Package:
jess.MultiFunctions
Arguments:
Multifield
Returns:
Integer
Description:
Returns the number of elements in the given multifield.

lexemep

Package:
jess.PredFunctions
Arguments:
Any value
Returns:
Boolean
Description:
Returns TRUE is the argument is an atom or string.

list-function$

Package:
(built-in)
Arguments:
None
Returns:
Multifield
Description:
Returns a multifield list of all the functions currently callable, including intrinsics, deffunctions, and Userfunctions. Each function name is an atom. The names are sorted in alphabetical order.

load-facts

Package:
(built-in)
Arguments:
A string or atom, the name of a file of facts
Returns:
Boolean
Description:
The argument should name a file containing a list of facts (not deffacts constructs, and no other commands or constructs.) Jess will parse the file and assert each fact. The return value is the return value of assert when asserting the last fact. In an applet, load-facts will use getDocumentBase() to find the named file.

load-function

Package:
jess.MiscFunctions
Arguments:
One string or atom, the name of a Java class
Returns:
Boolean
Description:
The argument must be the fully-qualified name of a Java class that implements the Userfunction interface. The class is loaded in to Jess and added to the engine, thus making the corresponding command available. See below, Extending Jess with Java for more information.

load-package

Package:
jess.MiscFunctions
Arguments:
One string or atom, the name of a Java class
Returns:
Boolean
Description:
The argument must be the fully-qualified name of a Java class that implements the Userpackage interface. The class is loaded in to Jess and added to the engine, thus making the corresponding package of commands available. See below, Extending Jess with Java for more information.

log

Package:
jess.MathFunctions
Arguments:
One number
Returns:
Number
Description:
Returns the natural logarithm of the argument.

log10

Package:
jess.MathFunctions
Arguments:
One number
Returns:
Number
Description:
Returns the base-10 logarithm of the argument.

lowcase

Package:
jess.StringFunctions
Arguments:
One atom or string.
Returns:
String
Description:
Returns the argument with all characters converted to lower case, as a string.

max

Package:
jess.MathFunctions
Arguments:
Two numbers
Returns:
Number
Description:
Returns the larger of the two arguments

member$

Package:
jess.MultiFunctions
Arguments:
A value and a multifield
Returns:
Integer or FALSE
Description:
Returns the 1-based index at which the value appears in the multifield, or FALSE if it does not appear.

min

Package:
jess.MathFunctions
Arguments:
Two numbers
Returns:
Number
Description:
Returns the lesser of the two arguments.

mod

Package:
(built-in)
Arguments:
Two integers
Returns:
Integer
Description:
Returns the integral remainder of dividing the first argument by the second.

modify

Package:
(built-in)
Arguments:
A fact-ID and any number of two-element lists
Returns:
Fact-ID
Description:
The fact-ID must belong to an unordered fact. Each list is taken as the name of a slot in this fact and a new value to assign to the slot. A new fact is asserted which is similar to the given fact but which has the specified slots replaced with new values. The original fact is retracted. The fact-ID of the new fact is returned. Modifying a 'definstance' fact will cause the appropriate object properties to be set as well.

multifieldp

Package:
jess.PredFunctions
Arguments:
Any value
Returns:
Boolean
Description:
Returns true if the argument is a multifield.

neq

Package:
(built-in)
Arguments:
Two or more values
Returns:
Boolean
Description:
Returns TRUE if the first argument is not equivalent (see eq) to any of the second or remaining arguments.

new

Package:
jess.reflect.ReflectFunctions
Arguments:
A string and any number of additional arguments
Returns:
Boolean
Description:
Creates a new Java object and returns an EXTERNAL_ADDRESS value containing it. The first argument is the fully-qualified class name: java.util.Vector, for example. The second and later arguments are constructor arguments. The constructor will be chosen from among all constuctors for the named class based on a "first-best fit" algorithm. Built-in Jess types are converted as necessary to match available constructors. See the text for more details.

not

Package:
(built-in)
Arguments:
A Boolean expression
Returns:
Boolean
Description:
Returns the Boolean opposite of the argument.

nth$

Package:
jess.MultiFunctions
Arguments:
A number and a multifield
Returns:
(Varies)
Description:
Returns the value at the given 1-based index of the multifield.

numberp

Package:
jess.PredFunctions
Arguments:
Any value
Returns:
Boolean
Description:
Returns true if the argument is a numeric type.

oddp

Package:
jess.PredFunctions
Arguments:
One integer
Returns:
Boolean
Description:
Returns TRUE if the argument is an odd number; see evenp.

open

Package:
(built-in)
Arguments:
A file name, an identifier for the file (an atom), and optionally a mode string: one of "r", "w", "a".
Returns:
The file identifier
Description:
Opens the named file for use by Jess. Subsequently, the given router identifier can be passed to printout, read, readline, or any other functions that accept I/O routers as arguments. By default, the file is opened for reading; if a mode string is given, it may be opened for reading only ("r"), writing only ("w"), or appending ("a").

or

Package:
(built-in)
Arguments:
Any number of function calls
Returns:
Boolean
Description:
Returns TRUE if any of the arguments evaluates to TRUE.

pi

Package:
jess.MathFunctions
Arguments:
None
Returns:
Number
Description:
Returns the number 'pi'.

ppdefrule

Package:
jess.MiscFunctions
Arguments:
Name of a rule (as a string or atom)
Returns:
String containing rule's text
Description:
Returns a pretty-print representation of a rule.

printout

Package:
(built-in)
Arguments:
The atom (a router identifier), followed by any number of additional values
Returns:
nil
Description:
Prints its arguments to the named router, which must be open for output. No spaces are added between arguments. The special atom 'crlf' prints as a newline. The special router name 't' can be used to signify standard output.

random

Package:
jess.MathFunctions
Arguments:
None
Returns:
Number
Description:
Returns a pseudo-random integer between 0 and 65536.

read

Package:
(built-in)
Arguments:
Optionally, an input router identifier (may be omitted, in which case 't' is the default).
Returns:
(Varies)
Description:
Reads a single atom, string, or number from the named router, returns this value. The router 't' means standard input.

readline

Package:
(built-in)
Arguments:
Optionally, an input router identifier (may be omitted, in which case 't' is the default).
Returns:
String
Description:
Reads a line from the named router, and returns it as a string. The router 't' means standard input.

replace$

Package:
jess.MultiFunctions
Arguments:
A multifield, two numbers, and another multifield
Returns:
Multifield
Description:
The second multifield is inserted into the first multifield, replacing elements between the 1-based indices given by the two numeric arguments, inclusive.
Example:
 Jess> (replace$ (create$ a b c) 2 2 (create$ x y z))
     (a x y z c)

reset

Package:
(built-in)
Arguments:
None
Returns:
TRUE
Description:
Removes all facts from the fact-list, removes all activations, then asserts the fact (initial-fact), then asserts all facts found in deffacts and initializes all defglobals.

rest$

Package:
jess.MultiFunctions
Arguments:
One multifield
Returns:
Multifield
Description:
Returns a new multifield consisting of all elements from the given multifield except the first.

retract

Package:
(built-in)
Arguments:
Any number of fact-IDs
Returns:
TRUE
Description:
Retracts the facts whose IDs are given. Retracting a definstance fact will result in an implict call to undefinstance for the corresponding object (the object will no longer be pattern-matched.)

return

Package:
(built-in)
Arguments:
One arbitrary value
Returns:
(Varies)
Description:
Returns the given value from a deffunction. Exits the deffunction immediately.

round

Package:
jess.MathFunctions
Arguments:
One number
Returns:
Integer
Description:
Properly rounds the given number and returns the nearest integer.

rules

Package:
(built-in)
Arguments:
None
Returns:
TRUE
Description:
Prints a list of all defrules.

run

Package:
(built-in)
Arguments:
None
Returns:
TRUE
Description:
Starts the inference engine. Jess will keep running until no more activations remain or 'halt' is called.

save-facts

Package:
(built-in)
Arguments:
A filename, and optionally an atom
Returns:
Boolean
Description:
Attempts to open the named file for writing, and then writes a list of all facts on the fact-list to the file. This file is suitable for reading with load-facts. If the optional second argument is given, only facts whose head matches this atom will be saved. Does not work in applets.

set

Package:
jess.reflect.ReflectFunctions
Arguments:
An external address, a string, and any value.
Returns:
The last argument
Description:
Sets a Java Bean's property to the given value. The first argument is the Bean object; the second argument is the name of the property. The third value is the new value for the property; the same conversions are applied as for new and call.

set-member

Package:
jess.reflect.ReflectFunctions
Arguments:
An external address or a string, a string, and any value.
Returns:
The last argument
Description:
Sets a Java object's member variable to the given value. The first argument is the object (or the name of the class, in the case of a static member variable); the second argument is the name of the variable. The third value is the new value for the variable; the same conversions are applied as for new and call.

setgen

Package:
jess.MiscFunctions
Arguments:
A number
Returns:
TRUE
Description:
Sets the integer which will be used by gensym* to generate the next unique symbol. Note that if this number has already been used, gensym* uses the next larger number that has not been used.

socket

Package:
jess.MiscFunctions
Arguments:
An Internet hostname, a TCP port number, and a router identifier
Returns:
The router identifier
Description:
Somewhat equivalent to open, except that instead of opening a file, opens an unbuffered TCP network connection to the named host at the named port, and installs it as a pair of read and write routers under the given name.

sqrt

Package:
jess.MathFunctions
Arguments:
A number
Returns:
Number
Description:
Returns the square root of the argument.

str-cat

Package:
jess.StringFunctions
Arguments:
Any number of values
Returns:
String
Description:
Converts all arguments to strings and concatenates them together, returning the result as a string.

str-compare

Package:
jess.StringFunctions
Arguments:
Two strings
Returns:
Integer
Description:
Returns 0 if the strings are identical, -1 if the first is lexically less than the second, +1 if lexically greater.

str-index

Package:
jess.StringFunctions
Arguments:
Two strings
Returns:
Integer or FALSE
Description:
Returns the 1-based index at which the first string first appears in the second; or FALSE if it does not appear.

str-length

Package:
jess.StringFunctions
Arguments:
A string
Returns:
Integer
Description:
Returns the length of the string in characters.

stringp

Package:
jess.PredFunctions
Arguments:
Any value
Returns:
Boolean
Description:
Returns TRUE if the argument is a string.

sub-string

Package:
jess.StringFunctions
Arguments:
Two numbers and a string
Returns:
String
Description:
Returns the string consisting of the characters between the two 1-based indices of the given string (inclusive).

subseq$

Package:
jess.MultiFunctions
Arguments:
A multifield and two numbers
Returns:
Multifield
Description:
Returns a multifield consisting of the elements between the two 1-based indices of the given multifield (inclusive).

subsetp

Package:
jess.MultiFunctions
Arguments:
Two multifields
Returns:
Boolean
Description:
Returns TRUE if all the elements of the first multifield appear in the second multifield.

sym-cat

Package:
(built-in)
Arguments:
Any number of values
Returns:
Atom
Description:
Converts all arguments to strings and concatenates them together, returning the result as an atom.

symbolp

Package:
jess.PredFunctions
Arguments:
Any value
Returns:
Boolean
Description:
Returns TRUE if the argument is an atom.

system

Package:
jess.MiscFunctions
Arguments:
Any number of values
Returns:
TRUE
Description:
Executes the operating-system command-line constructed by converting each argument to a string. Normally blocks (Jess stops until applet returns), but if last argument is an ampersand (&), the program will run in the background.

time

Package:
jess.MiscFunctions
Arguments:
None
Returns:
Number
Description:
Returns the number of seconds since 12:00 AM, Jan 1, 1970.

undefrule

Package:
(built-in)
Arguments:
An atom (the name of a rule)
Returns:
Boolean
Description:
Remove the named rule from the Rete network. This rule will never fire again. Returns TRUE if the rule existed.

union$

Package:
jess.MultiFunctions
Arguments:
Two multifields
Returns:
Multifield
Description:
Returns a new multifield consisting of all the elements that appear in the two arguments; duplicates are removed.

unwatch

Package:
(built-in)
Arguments:
One of the atoms all, rules, compilations, activations, facts
Returns:
TRUE
Description:
Causes trace output to not be printed for the given indicator. See watch

upcase

Package:
jess.StringFunctions
Arguments:
A string or atom
Returns:
A string
Description:
Returns the argument as an all-uppercase string.

view

Package:
jess.view.ViewFunctions
Arguments:
None
Returns:
TRUE
Description:
This Userfunction is included in the Jess distribution but is not normally installed; you must load it using load-package (the class name is jess.view.ViewFunctions). It requires Java 1.1. When invoked, it displays a live snapshot of the Rete network in a graphical window. The display is described below, in the section entitled How Jess Works.

watch

Package:
(built-in)
Arguments:
One of the atoms all, rules, compilations, activations, facts
Returns:
TRUE
Description:
Produces additional debug output when specific events happen in Jess, depending on the argument. Any number of different watches can be active simultaneously.

while

Package:
(built-in)
Arguments:
A Boolean value or a function call returning Boolean, the atom 'do', and an arbitrary number of additional function calls.
Returns:
(Varies)
Description:
Evaluates the boolean expression repeatedly. As long as it does not equal FALSE, the list of other expressions are evaluated. The last expression evaluated is the return value.

4 Writing Jess Code

Many useful expert systems can be written using only the Jess language as presented above. I won't present a tutorial on writing such system here (maybe someday!) but I do want to share a few useful hints and ideas.

4.1 Using an External Editor

Jess allows you to enter rules directly at its interactive prompt. While this is fine for experimenting, Jess doesn't yet have the ability to remember the source text for all the rules and constructs you enter. Therefore, you will typically enter your rules and other data into a separate script file and read it into Jess using the batch command. Jess does offer the ppdefrule and save-facts commands, both of which can be very helpful in interactively building up a system definition and them storing it in a file.

4.2 Efficiency

The single biggest determinant of Jess performance is the number of partial matches generated by your rules. You should always try to obey the following (sometimes contradictory) guidelines while writing your rules: You can use the view command to find out how many partial matches your rules generate. First you'll need to read the section entitled How Jess Works.

4.3 Error Reporting and Debugging

I've tried hard to improve Jess' syntax error reporting in this release, but it is still not as detailed as it could be. When you get an error from Jess (during parsing or at runtime) it is generally delivered as a Java exception. The exception will contain an explanation of the problem, and the stack trace of the exception will help you understand what went wrong. For example, if you attempt to load the folowing rule in Jess:

        Jess> (defrule foo-1
                (foo bar)
              ->
                (printout "Found Foo Bar" crlf))      
You'll get the following printout:
        Rete Exception in routine Jesp::parseDefrule.
          Message: Expected '=>' at line 3:  ( defrule foo ( foo bar ) - .
                at java.lang.Throwable.(Compiled Code)
                at java.lang.Exception.(Compiled Code)
                at jess.ReteException.(Compiled Code)
                at jess.ParseException.(Compiled Code)
                at jess.Jesp.parseError(Compiled Code)
                at jess.Jesp.doParseDefrule(Compiled Code)
                at jess.Jesp.parseDefrule(Compiled Code)
                at jess.Jesp.parseSexp(Compiled Code)
                at jess.Jesp.parse(Compiled Code)
                at jess.Main.main(Compiled Code)
Looking at the routine names listed in the stack trace make it fairly clear that a Defrule was being parsed, and the detail message explains that the position of the '.' was reached in the input without finding the expectyed '=> symbol (we accidentally typed '->' instead.)

Runtime errors can be more puzzling, but the stack trace will give you a lot of information. Here's a rule where we erroneously try to add the number 3.0 to the word 'four':

        Jess> (defrule foo-2
              =>
                (printout t (+ 3.0 four) crlf))
When we (reset) and (run) we'll see:
        Rete Exception in routine Value::numericValue.
          Message: Not a number: four type = 1 at line 5:  ( run ) .
                at java.lang.Throwable.(Compiled Code)
                at java.lang.Exception.(Compiled Code)
                at jess.ReteException.(Compiled Code)
                at jess.Value.numericValue(Compiled Code)
                at jess._plus.call(Compiled Code)
                at jess.Funcall.simpleExecute(Compiled Code)
                at jess.Funcall.execute(Compiled Code)
                at jess.Funcall.execute(Compiled Code)
                at jess.Funcall.execute(Compiled Code)
                at jess.Defrule.fire(Compiled Code)
                at jess.Activation.fire(Compiled Code)
                at jess.Rete.run(Compiled Code)
                at jess.Rete.run(Compiled Code)
                at jess._run.call(Compiled Code)
                at jess.Funcall.simpleExecute(Compiled Code)
                at jess.Funcall.execute(Compiled Code)
                at jess.Funcall.execute(Compiled Code)
                at jess.Jesp.parseSexp(Compiled Code)
                at jess.Jesp.parse(Compiled Code)
                at jess.Main.main(Compiled Code)
In this case, the error message is pretty clear, except for the claim that the offending statement is 'run'. To find out what was really happening, we have to look at the stack trace. Starting from the top down, we find Value.numericValue() was called by _plus.call(). A few levels down, we see Defrule.fire(). Taken together, this means that an addition operation on the RHS of a rule found the symbol four as one of its operands, when it wanted a number.

The notation 'type = 1' in the error message, by the way, refers to a set of constants in the class jess.RU. The values of these constants are given in a later Section of this document. Consulting that table, we see that type 1 is RU.ATOM, a symbol, which is indeed not a number.

If we make a similar mistake on the LHS of a rule:

        Jess> (defrule foo-3
                (test (eq 3 (+ 2 one)))
                 =>
                )
We see the following after a (reset):
        Rete Exception in routine Value::numericValue.
          Message: Not a number: one type = 1 at line 17:  ( reset ) .
                at java.lang.Throwable.(Compiled Code)
                at java.lang.Exception.(Compiled Code)
                at jess.ReteException.(Compiled Code)
                at jess.Value.numericValue(Compiled Code)
                at jess._plus.call(Compiled Code)
                at jess.Funcall.simpleExecute(Compiled Code)
                at jess.Funcall.execute(Compiled Code)
                at jess.Funcall.execute(Compiled Code)
                at jess.Funcall.execute(Compiled Code)
                at jess.NodeTest.runTests(Compiled Code)
                at jess.NodeTest.callNode(Compiled Code)
                at jess.Node.passAlong(Compiled Code)
                at jess.Node1TELN.callNode(Compiled Code)
                at jess.Node.passAlong(Compiled Code)
                at jess.Node1TECT.callNode(Compiled Code)
                at jess.Rete.processTokenOneNode(Compiled Code)
                at jess.Rete.processToken(Compiled Code)
                at jess.Rete.assert(Compiled Code)
                at jess.Rete.reset(Compiled Code)
                at jess._reset.call(Compiled Code)
                at jess.Funcall.simpleExecute(Compiled Code)
                at jess.Funcall.execute(Compiled Code)
                at jess.Funcall.execute(Compiled Code)
                at jess.Jesp.parseSexp(Compiled Code)
                at jess.Jesp.parse(Compiled Code)
                at jess.Main.main(Compiled Code)
Again, the error message is somewhat but not completely helpful, and the stack trace contains additional information. Here we see our old friends Value.numericValue() and _plus.call() being called, but we don't see the Defrule being fired; instead wee see lots of oddly named classes and functions with names containing Node and Token. This is always a tip-off that the error happened on Defrule LHS processing. Way down the stack we see Rete.assert() being called by Rete.reset(), which also indicates that LHS processing was in progress when the exception happened.

5 Embedding Jess in a Java Program

There are three different ways to use Jess and Java code together:

5.1 The jess.Rete Class

5.1.1 Executing a File of Jess Code
Using Jess from Java code is simple. The jess.Rete class contains the expert system engine. The jess.Jesp class contains the Jess parser. To execute a file of CLIPS code in Jess (like the Jess 'batch' command), simply create a Rete object and a Jesp object, tell the Jesp object about the file, and call Jesp.parse(boolean prompt):
  
import jess.*;

    // ...

    // See info about the ReteDisplay classes below
    NullDisplay nd = new NullDisplay();

    // Create a Jess engine
    Rete rete = new Rete(nd);

    // Open the file test.clp
    FileInputStream fis = new FileInputStream("test.clp");

    // Create a parser for the file, telling it where to take input
    // from and which engine to send the results to
    Jesp j = new Jesp(fis, rete);
    try
      {
        // parse and execute the code, without printing a prompt
        j.parse(false);
      }
    catch (ReteException re)
      {
        // All Jess errors are reported as 'ReteException's.
        re.printStackTrace(nd.stderr());
      }

Note that if the file 'test.clp' contains the CLIPS (reset) and (run) commands, the Jess engine will run to completion during the parse() call. Also note that Jess will throw jess.ReteException exceptions to signal errors.
5.1.2 Executing Individual Commands
For somewhat more control over Jess from your Java program, you can use the Rete class's executeCommand(String cmd) method. For example, after the above code, you could include the following:
    try
      {
        rete.executeCommand("(reset)");
        rete.executeCommand("(assert (foo bar foo))");
        rete.executeCommand("(run)");
      }
    catch (ReteException ex)
      {
        System.err.println("Foo bar error.");
      }
Commands executed via executeCommand() may refer to Jess variables; they will be interpreted in the global context. In general, only defglobals can be used in this way.

5.1.3 Other Methods in the Rete Class
Rete has a number of other public methods which I have not documented here; see the source for details. There are functions to assert and retract facts (which you invited to use) and functions to find and remove various constructs (which you may also use.) There are also functions which allow you to add constructs (defrules, deftemplates, etc.) In general, it is best that you not use these in your programs, because they are highly subject to change in future Jess versions, as are the details of the classes like jess.Defrule which represent constructs. Two other functions you will certainly use are addUserfunction() and addUserpackage(); these will be explained below.

There is a set of diagnostic methods in Rete which return Enumerations of various data structures in the engine:

Feel free to use these for debugging purposes, but don't get too chummy with them: they are subject to change without notice, especially the types of the objects they return.

5.2 The ReteDisplay Interface.

There is a lot you can do with just what we've discussed so far. One thing that you might not like, though, is that by default, programs we write using the above techniques will send their output to your Java program's System.out and take input from System.in. If you're writing a graphical program, this is clearly not what you want. Jess provides an interface jess.ReteDisplayReteDisplay that lets you deal with this in a simple way. ReteDisplay also provides some hooks into the engine's internal workings. The ReteDisplay interface provides two types of functions:
  1. Functions that return the intial input, output, and error streams that the engine should use
  2. Functions that are called by the engine whenever an event occurs (events here meaning a construct is parsed, fact is asserted, rule is activated, etc.)
5.2.1 Using ReteDisplay
Every Rete object must be constructed with an instance of ReteDisplay. Here is the definition of the ReteDisplay interface:
        public interface ReteDisplay
        {
          // Rete sets its initial input/output using these        
          // These must be implemented
          public java.io.PrintStream stdout();
          public java.io.InputStream stdin();
          public java.io.PrintStream stderr();

          // These notify the ReteDisplay object when things happen
          // Can do nothing if you want!
          public void assertFact(ValueVector fact);
          public void retractFact(ValueVector fact);
          public void addDeffacts(Deffacts df);
          public void addDeftemplate(Deftemplate dt);
          public void addDefrule(Defrule rule);
          public void activateRule(Defrule rule);
          public void deactivateRule(Defrule rule);
          public void fireRule(Defrule rule);

          // This gives the Rete object access to Applet resources
          // Should return null if not in an Applet
          public java.applet.Applet applet();
        }
Jess ships with three different classes that implement this interface. You can write your own, or modify these, for use in your own applications.
5.2.2 The jess.TextAreaOutputStream and jess.TextInputStream Classes
Jess ships with two utility classes that are very useful when building GUIs for Jess: the jess.TextAreaOutputStream and jess.TextInputStream classes. Both can serve as adapters between Jess and graphical input/output widgets. The TextAreaOutputStream class is, as the name implies, a Java OutputStream that sends any data written to it to a java.awt.TextArea. This lets you place Jess's output in a scrolling window on your GUI. The Applet "personality" of the jess.Main class uses TextAreaOutputStream. To use it, simply write a class that implements ReteDisplay, returning a suitably wrapped TextAreaOutputStream from the stdout() and stderr() methods:
        class MyDisplay
        {        
           public TextArea ta = new TextArea(20, 80);
           TextAreaOutputTream taos = new TextAreaOutputStream(ta);
           PrintStream ps = new PrintStream(taos, true);
           
           MyDisplay()
           {
             Frame f = new Frame("Jess Demo");
             f.add(ta);
             f.pack();
             f.show();
           }

           PrintStream stdout() { return ps; }

        // ...
Now if you construct a jess.Rete object with an instance of this display, the Jess output will go into a scrolling window. Study jess/Main.java and jess/LostDisplay.java to see a complete example of this.

jess.TextInputStream is similar, but it is an InputStream instead. It is actually quite similar to java.io.StringBufferInputStream, except that you can continually add new text to the 'end' of the stream (using the appendText() method.) It is intended that you create a jess.TextInputStream, return it from your jess.ReteDisplay.stdin() method, and then (in an AWT event handler, somewhere) append new input to the stream whenever it becomes available. See jess/QuizDisplay.java for a complete usage example.

5.2.3 Switching Streams in Mid-Stream
The Rete engine will call the stdout() and stdin() methods of your ReteDisplay class at least once, when it is created: it is from these return values that the initial definition of the 't', 'WSTDOUT', and 'WSTDERR' I/O routers are made (see below.) It may never call these methods again; this means that changing the return values of these functions over time may have no effect. To reroute input and output, you must use the explicit router functions.

5.3 Manipulating Jess I/O Routers in Java

ReteDisplay lets you set up the minumal initial state for a Rete object, but Jess can read from more that just standard input and standard output. Jess I/O routers can be easily manipulated from Java. There are six functions in the Rete class that manipulate the router list:

When Jess starts up, there are one input router and three output routers defined: the 't' router, which reads and writes from the standard input and output; the WSTDOUT router, which Jess uses for all prompts, diagnostic outputs, and other displays; and the WSTDERR router, which Jess uses to print stack traces and error messages. You can reroute these inputs and outputs simply by changing the Input and Output streams they are attached to using the above functions. You can use any kind of streams you can dream up: network streams, file streams, etc.

The Rete class has two more handy router-related methods: outStream() and errStream(), both of which return a PrintStream object. outStream returns a stream that goes to the same place as the current setting of WSTDOUT; errStream() does the same for WSTDERR.

You can add your own routers which do I/O through any Java streams; they will immediately be usable from Jess. Look at the implementation of the socket Userfunction in jess/MiscFunctions.java for an idea of what's possible.

5.4 Calling Jess Functions and Getting Results Back

So far we have treated the Rete class more-or-less as a "black box:" we have only poked and prodded it from the outside, without exchanging any real data, only strings of text. To achieve a tight integration between Jess and your application, you'll need to be able to go further. Most likely, you'll want to execute commands in the Jess language that accept arguments that cannot be easily or correctly represented in the String argument to executeCommand(): for example, a floating-point number, or a Java object, or a multifield value, or a fact. It's actually quite easy to call any Jess function, passing in any data as arguments, and receiving the result not as a string but as arbitrary data. To do this, you're going to have to leanr about two more Jess classes: jess.Value and jess.ValueVector.
5.4.1 The class jess.Value
A Value is a self-describing data object. Every datum in Jess is contained in one. Once it is constructed, a Value's type and contents cannot be changed. Valuesupports a type() function, which returns one of these type constants (defined in the class jess.RU, (RU = "Rete Utilities")):
 
  final public static int NONE             =     0; ; an empty value (not NIL)
  final public static int ATOM             =     1; ; a symbol
  final public static int STRING           =     2; ; a string
  final public static int INTEGER          =     4; ; an integer
  final public static int VARIABLE         =     8; ; a variable
  final public static int FACT_ID          =    16; ; a fact index
  final public static int FLOAT            =    32; ; a double float
  final public static int FUNCALL          =    64; ; a function call
  final public static int ORDERED_FACT     =   128; ; an ordered fact
  final public static int UNORDERED_FACT   =   256; ; a deftemplate fact
  final public static int LIST             =   512; ; a multifield
  final public static int DESCRIPTOR       =  1024; ; (internal use)
  final public static int EXTERNAL_ADDRESS =  2048; ; a Java object
  final public static int INTARRAY         =  4096; ; (internal use)
  final public static int MULTIVARIABLE    =  8192; ; a multivariable
  final public static int SLOT             = 16384; ; (internal use)
  final public static int MULTISLOT        = 32768; ; (internal use)
Value objects are constructed by specifying the data and the type. Each overloaded constructor assures that the given data and the given type are compatible. Note that for each constructor, more than one value of the type parameter is acceptable. The available constructors are:
  public Value(Object o, int type) throws ReteException 
  public Value(String s, int type) throws ReteException 
  public Value(Value v) 
  public Value(ValueVector f, int type) throws ReteException 
  public Value(double d, int type) throws ReteException 
  public Value(int value, int type) throws ReteException 
  public Value(int[] a, int type) throws ReteException
Value supports a number of functions to get the actual data out of a Valueobject. These are
  public Object externalAddressValue() throws ReteException
  public String stringValue() throws ReteException 
  public ValueVector factValue() throws ReteException 
  public ValueVector funcallValue() throws ReteException 
  public ValueVector listValue() throws ReteException 
  public double floatValue() throws ReteException 
  public double numericValue() throws ReteException 
  public int atomValue() throws ReteException 
  public int descriptorValue() throws ReteException 
  public int factIDValue() throws ReteException 
  public int intValue() throws ReteException 
  public int variableValue() throws ReteException 
  public int[] intArrayValue() throws ReteException
If you try to convert random values by creating a Value and retrieving it as some other type, you'll generally get a ReteException. However, many types can be freely interconverted: Strings and atoms, for example, or integers and floats.

Note that Jess stores all strings, atoms and variable names as integers, which are used as indexes into a hashtable. Thus if Value.type() returns RU.ATOM or RU.STRING, you can call either atomValue() (which returns that integer) or stringValue() (which returns a Java String object.) To convert a String to an appropriate integer, call the static function int RU.putAtom(String). To get the String that goes with an integer, call String RU.getAtom(int). Note that this is NOT a way to convert the String "1" to the integer 1; it converts Strings into unique hash codes.

5.4.2 The class jess.ValueVector
Facts, function calls, lists, etc. are stored by Jess in objects of class jess.ValueVector. ValueVector is an extensible array of Value objects. You set an element of a ValueVector with void set(Value, int) and get an element with Value get(int). set() and get() will throw an exception if the index you're accessing is past the end of the current array. You can add a value to the end of a ValueVector with void add(Value) (which can extend the length of the internal data structures.) int size() returns the actual number of Values in the ValueVector. void setLength(int) lets you cheat by extending the length of a ValueVector to include null Values. (This is necessary sometimes to allow filling in many elements in random order.)

Facts (type RU.ORDERED_FACT or RU.UNORDERED_FACT) are stored as a ValueVector with the slots filled in a special way, as follows (the constants representing slot numbers MUST be used, as they may change)
SLOT NUMBER TYPE DESCRIPTION
RU.CLASS RU.ATOM The 'head' or first field of the fact
RU.ID RU.FACT_ID The fact-id of this fact
RU.DESC RU.DESCRIPTOR One of RU.ORDERED_FACT or RU.UNORDERED_FACT
RU.FIRST_SLOT (ANY) Value of the first slot of this fact
RU.FIRST_SLOT + 1 (ANY) second
... ... ...
Note that for ordered facts, the slots are stored in the order in which they appear, but in unordered (deftemplate) facts, they appear in the order given in the corresponding deftemplate.

Function calls (RU.FUNCALLs) are simpler; the first slot is the functor as an RU.ATOM, and all remaining slots are arguments. And multifields are even simpler: each element of the ValueVector is an element of the multifield.

5.4.3 Creating and Executing RU.FUNCALL Objects
Given the above, it is very easy to create and execute objects that represent Jess function calls in Java. This allows you to pass arbitrary arguments, and to receive the return value. The steps to follow are:
  1. Create a jess.Funcall object (a subclass of ValueVector)
  2. Append the desired arguments as Value objects
  3. Call Funcall.simpleExecute() to execute the command.
  4. The return value is a Value object containing the result.
For example, here we are calling the Jess "bind" function, to make the defglobal named "*x*" point to a Java object of the imaginary type Foo:
    Rete engine = ...;
    Foo foo = new Foo();

    // Define the defglobal
    engine.executeCommand("(defglobal ?*x* = 0)");

    // Create the Funcall object
    Funcall f = new Funcall("bind", engine);

    // Now add the arguments, in left-to-right order. Each argument
    // is a jess.Value object. Note the use of the
    // RU.EXTERNAL_ADDRESS  type to pass in the Java object
    f.add(new Value(RU.putAtom("*x*"), RU.VARIABLE));
    f.add(new Value(foo, RU.EXTERNAL_ADDRESS));

    // Now execute the function (simpleExecute is a static function.)
    f.simpleExecute(f, rete.globalContext());

    // Now Jess code has access to the Java object!
    engine.executeCommand("(printout t ?*x* crlf)");
There are a few restrictions on what you can do with this technique:

5.5 Using Jess in a Multithreaded Program

I think that Jess is now entirely thread-safe, as long as you use it in a reasonable fashion. One major difference between Jess and CLIPS is that you can call (run) from a rule RHS and have a new rule fired up in the middle of RHS execution! This would almost certainly cause problems, so you probably shouldn't do it.

Jess maintains separate mutexes for RHS execution and for LHS execution; in other words, only one assert or retract may be going on in a single engine at once, and only one rule RHS may be executing at once; however, these two activities may occur simultaneously.

If you use Java object matching on rule LHSs, be aware of interactions between multiple threads. It is certainly possible to come up with combinations of objects and rule engines that will deadlock; the easiest way to do this is to trigger a PropertyChangeEvent from a rule LHS, so be careful not to do that!


6 Extending Jess With Commands Written in Java

Jess's rule language can be extended with additional commands written in Java. For many real applications, extending Jess in this way may be necessary. The good news is that it's very easy, and you can add capabilities to Jess limited only by your imagination.

6.1 Extension Objects

The Java interface jess.Userfunction represents a single user-supplied function, while the interface jess.Userpackage represents a whole set of such functions. When you write a new function for Jess, you do it by writing a class that implements the jess.Userfunction interface (see below for details on how this is done.) Then a single instance of this class is created and installed into Jess. These objects can maintain state and can be retrieved by other code you write (see below.) Therefore a Userfunction can cache results across invocations, maintain complex data structures, or keep references to external Java objects for callbacks.

6.2 Installing Extensions

Given that you have written or obtained some classes which implement these interfaces, you can load these extensions into Jess in two ways. First, you can load them in from Java code. Given that 'rete' is the Rete object in your application, and 'MyFunction' is the name of a Userfunction class you (or someone else!) wrote, you can add the new function to Jess by calling
        rete.addUserfunction(new MyFunction());
or an entire package of such functions in a class 'mypackage' using
 
        rete.addUserpackage(new MyPackage());
You can also load extension functions and packages from the Jess language itself. The equivalents to the above are
        (load-function "myfunction")
and
        (load-package "mypackage")
Note that if the new classes or user packages come in a Java package, you'll need to specify the fully qualified name of the class:
  (load-package "xyzzy.bassomatic.mypackage")
In any case, the relevant classes need to be reachable on your Java CLASSPATH.

6.3 Writing Extensions

I've made it as easy as possible to add user-defined functions to Jess. There is no system type-checking on arguments, so you don't need to tell the system about your arguments, and values are self-describing, so you don't need to tell the system what type you return. You do, however, need to understand several Jess classes: jess.Value, jess.ValueVector, and jess.Funcall, as discussed in the previous Section.

To implement the jess.Userfunction interface, you need to implement only two methods: name() and call(). Here's an example of a class called 'MyUpcase' that implements the Jess function my-upcase, which expects a String as an argument, and returns the string in uppercase.

  import jess.*;
  public class MyUpcase implements Userfunction
    {
      // The name method returns the integer representation of the name.
      // This function will be called by Jess.
      public int name() { return RU.putAtom("my-upcase"); }

      public Value call(ValueVector vv, Context context) throws ReteException
        {
          return new Value(vv.get(1).stringValue().toUpperCase(), RU.STRING);
        }
    }
The name() function returns an integer that represents the name of the function. You must obtain the appropriate integer using the static function int RU.putAtom(String s). name() almost always looks exactly like this.

The call() method does the business of your Userfunction. When call() is invoked, the first argument will be a ValueVector representation of the Jess code that evoked your function. For example, if the following Jess function call was made,

        (my-upcase "foo")
the first argument to call() would be a ValueVector of length two. The first element would be a Value containing the symbol (type RU.ATOM) "my-upcase", and the second argument would be a Value containing the string (RU.STRING) "foo".

Note that we use vv.get(1).stringValue() to get the first argument to "my-upcase" as a Java String. If the argument doesn't contain a string, or something convertible to a string, a ReteException may be thrown that describes the problem; hence you don't need to worry about incorrect argument types if you don't want to. vv.get(0) will always return the symbol "my-upcase", the name of the function being called (the clever programmer will note that this would let you construct multiple objects of the same class, implementing different functions based on the name of the function passed in as a constructor argument); vv.get(1) is the first argument, and vv.get(2) would be the second, if this function accepted multiple arguments. If you want, you can check how many arguments your function was called with and throw a ReteException if it was the wrong number by using the vv.size() method. In any case, our simple implementation extracts a single argument and uses the Java toUpperCase() method to do its work. call() must wrap its return value in a jess.Value object, specifying the type (here it is RU.STRING).

Having written this class, you can then, in your mainline code, simply call Rete.addUserfunction() with an instance of your new class as an argument, and the function will be available from Jess code. Adding to our mainline code from the last section:

  // Add the 'my-upcase' command to Jess
  rete.addUserfunction(new MyUpcase());
  // Exceute some Jess code that calls this function
  rete.executeCommand("(printout t (my-upcase foo) crlf)");
will print "FOO".

6.4 Writing Extension Packages

The jess.Userpackage interface is a handy way to group a collection of Userfunctions together, so that you don't need to install them one by one (all of the extensions shipped with Jess are included in Userpackage classes.) A Userpackage class should supply the one method add(), which adds a collection of Userfunctions to a Rete engine using addUserfunction(). Nothing mysterious going on, but it's very convenient. As an example, the string utilities package jess/StringFunctions.java looks something like this:
   
        public class StringFunctions implements Userpackage
        {
          public void add(Rete engine)
          {
              engine.addUserfunction(new strcat());
              engine.addUserfunction(new upcase());
              engine.addUserfunction(new lowcase());
              engine.addUserfunction(new strcompare());
              engine.addUserfunction(new strlength());
              engine.addUserfunction(new substring());

          }
        }
Now in your mainline, you can call
        engine.addUserpackage(new jess.StringFunctions());
and from your Jess code, you can call str-cat, str-compare, etc.

Userpackages are a great place to assemble a collection of interrelated functions which potentially can share data or maintain references to other function objects. You can also use Userpackages to make sure that your Userfunctions are constructed with the correct constructor arguments.

There are a lot of small Userfunction classes in the jess package which you can use as examples for writing your own Jess extensions. All of these small classes can add a lot of size overhead to Applets, which is why they are not all built-in to the engine. These days, with zips and JAR files, this isn't such a big deal. Still, you can leave them out if you want just by not adding the relevant Userpackage from your mainline program. Contrarily, if you don't add these packages to your programs, the corresponding Jess functions will not be available.

6.5 Obtaining References to Userfunction Objects

Occasionally it is useful to be able to obtain a reference to an installed Userfunction object. The method Userfunction Rete.findUserFunction(String name) lets you do this easily. It returns the Userfunction object registered under the given name, or null if there is none. This is most useful when you write Userfunctions which themselves maintain state of some kind, and you need access to that state.

7 Accessing Java Objects Directly from Jess

As of Jess 4.0, you can create Java objects, call their methods, and access their data directly from Jess without writing any Java code! The Jess functions that enable this are in the optional Userpackage jess.reflect.ReflectFunctions and they require Java 1.1 or later to compile. See the Jess Function Guide for a description of the call, new, set, get, set-member and get-member methods.

This new capability makes Jess more than just an expert system shell; it is now also a dynamic, extensible, portable Java-based scripting environment.

7.1 Creating Java Objects

The Jess new function lets you create Java objects. The first argument is the fully-qualified name of the class as a symbol or String; any remaining arguments are passed to the Java object's constructor. For example:
        (new java.lang.StringBuffer 100)
will create an instance of StringBuffer, passing the integer 100 as a constructor argument.

In the case of overloaded constructors, the constructor will be chosen from among all constuctors for the named class with the given number of arguments based on a "first-best fit" algorithm. Built-in Jess types are converted as necessary to match the available constructors. The Jess atoms TRUE and FALSE are automatically converted to Java booleans, while the atom nil is automatically converted to the Java null pointer. Jess multifields are automatically converted to one-dimensional Java arrays; there is no way to represent a multidimensional Java array in Jess (if this becomes necessary, you can always write a Userfunction to call the appropriate constructor.) Floating point values are converted to the best-matching floating-point type. External address types are unwrapped and passed directly as Java arguments. If you have trouble calling the correct constructor, you can often disambiguate between multiple constructors by using Java wrapper objects. For example, if the imaginary Foo class had the two constructors

        Foo(double d);
        Foo(float f);
you could specifically call the one that takes a float argument instead of the double argument like this: way:
        (new Foo (new java.lang.Float 123.456))

7.2 Calling Java Methods

The Jess call function lets you call Java methods. The first argument to call is either a variable holding the Java object on which to call the method, or the name of the class (for a static method.) The second argument is the name of the method. The remaining arguments are the arguments to the method. Jess will find an appropriate method using the same techniques descibed above.

An example: to use the void java.lang.StringBuffer.append(String s) method directly, you can write

    
        (defglobal ?*str-buf* = (new java.lang.StringBuffer 100))
        (call ?*str-buf* append "Some String Data To Append")
        
Note that in many cases, explicit use of the call functor is optional; it can be omitted in function calls that are not nested inside of other function calls. For example, the above call to append could also be written as
    
        (?*str-buf* append "Some String Data To Append")        
A static method example: you can invoke the Java garbage collector using the java.lang.System.gc() method like this:
        (call java.lang.System gc)

7.3 Setting and Reading Java variables

You can set and read the values of public Java member variables using the set-member and get-member functions. Each of these functions accepts either a Java object or a class name (in the case of static members) as the first argument. The second argument is the name of the field. set-member requires a third argument, the new value for the field. There is no mechanism for setting or getting a specific member of an array field; you can only set or read complete arrays, which are represented in Jess as multifield values.

An example: if ?pt holds a java.awt.Point object, you can reference its x coordinate field like this:

        (printout t "The value of x is " (get-member ?pt x))

7.4 Setting and Reading Java Bean Properties

As described previously, Java objects can be explicitly pattern-matched on the LHS of rules, but only to the extent that they are Java Beans. A Java Bean is really just a Java object that has a number of methods that obey a simple naming convention for Java Bean properties. A class has a Bean property if, for some string X and type T it has either or both of: Note that the capitalization is also important: for example, for a method named isVisible, the property's name is visible, with a lower-case 'V'. Only the capitalization of the first letter of the name is important. You can conveniently set and get these properties using the Jess set and get methods. Note that many of the trivial changes in the Java 1.1 were directed towards making most visible properties of objects into Bean properties.

An example: AWT components have many Bean properties. One is visible, the property of being visible on the screen. We can query this property in two ways: either by explicitly calling the isVisible() method, or by querying the Bean property.

        (defglobal ?*frame* (new java.awt.Frame "Frame Demo"))

        ; Directly call 'isVisible', or...
        (printout t (call ?*frame* isVisible) crlf)

        ; ... equivalently, query the Bean property
        (printout t (get ?*frame visible) crlf)

7.5 Writing Java GUIs in Jess

One special case of manipulating Java objects directly from Jess is the building of Java GUIs from Jess code. I think this will be pretty common, so I've provided some utilities for extending what is possible. All of these utility classes are in the jess.reflect package.

It should now be obvious that you can easily construct GUI objects from Jess; for example, here is a Button:

        (defglobal ?*b* = (new java.awt.Button "Hello"))
What should not be obvious is how, from Jess, you can arrange to have somthing happen whan the button is pressed. For this, I have provided a full set of EventListener classes: Each of these classes implements one of the Listener interfaces from the java.awt.event package in Java 1.1. Each implementation packages up any event notifications it receives and forwards them to a Jess deffunction, which is supplied as a constructor argument to the Listener object.

An example should clarify matters. Let's say that when the "Hello" button is pressed, you would like the string "Hello, World!" to be printed to standard output (how original!) What you need to do is:

  1. Define a deffunction which prints the message. The deffunction will be called with one argument: the event object that would be passed to actionPerformed() (If this is gibberish to you, you should pick up a book on Java/AWT programming.)
  2. Create a jess.reflect.ActionListener object, telling it about this deffunction, and also which Jess engine it belongs to.
  3. Tell the Button about the ActionListener using the addActionListener method of java.awt.Button.
Here's a complete program in Jess:
        (defglobal ?*f* = (new java.awt.Frame "Button Demo"))
        (defglobal ?*b* = (new java.awt.Button "Hello"))
        
        (deffunction say-hello (?evt)
          (printout t "Hello, World!" crlf))

        (?*b* addActionListener
          (new jess.reflect.ActionListener say-hello (engine)))
       
        (?*f* add ?*b*)
        (?*f* pack)
        (set ?*f* visible TRUE)        
The Jess engine function returns the jess.Rete object in which it is executed, as an external address. You'll have to quit using ^C. To fix this, you can add a WindowListener which handles WINDOW_CLOSING events to the above program:

        (deffunction frame-handler (?evt)
          (if (= (?evt getID) (get-member ?evt WINDOW_CLOSING)) then 
            (call (get ?evt source) dispose)
            (exit)))

        (?*f* addWindowListener
          (new jess.reflect.WindowListener frame-handler (engine)))
Now when you close the window Jess will exit. Notice how we can examin the ?evt parameter for event information.

See the demo examples/frame.clp for a slightly more complex example of how you can build an entire Java graphical interface from within Jess.

7.6 Efficiency

This reflection capability is extremely powerful and useful; however, such function calls will not be as efficient as calls made to the same Java function through a compiled Userfunction interface. For example, the two Jess function calls
(pi)
and
(get-member java.lang.Math PI)
both return the value of the static final member PI of the class java.lang.Math, but the first call is much more efficient because (pi) is a Userfunction. If you are going to call a Java function just once, go ahead and use call, but if you're going to use it in a loop, consider wrapping it in a Userfunction if performance might be an issue.

8 How Jess Works

Note: the information in this Section is provided for the curious reader. An understanding of the Rete algorithm may be helpful in planning expert systems; an understanding of Jess' implementation probably will not be. Feel free to skip this Section and come back to it some other time. You should not take advantage of many of the Java classes mentioned in this Section; they are internal implementation details, and any Java code you write which uses them may well break each time a new version of Jess is released.

Jess is a rule-based expert system shell. In the simplest terms, this means that Jess's purpose it to continuously apply a set of if-then statements, called rules, to a set of data, called the fact list. You define the rules that make up your own particular expert system. Rules in Jess look something like this:

  (defrule library-rule-1
    (book (name ?X) (status late) (borrower ?Y))
    (borrower (name ?Y) (address ?Z))
   =>
    (send-late-notice ?X ?Y ?Z))
Note that this syntax is borrowed from (and is identical to) the syntax used by CLIPS. This rule might be translated into psueudo-english like this:
   Library rule #1:
   If
     a late book exists, with name X, borrowed by someone named Y
   and
     that borrower's address is known to be Z
   then
     send a late notice to Y at Z about the book X.
The book and borrower entities would be found on the fact list. The fact list is therefore a kind of database of bits of factual knowledge about the world. The attributes (called "slots") that things like books and borrowers are allowed to have are defined in statements called "deftemplates"; actions like send-late-notice can be defined in user-written functions in the Jess language ("deffunctions") or in Java ("Userfunctions.") For more information about the CLIPS rule syntax (and to work with Jess, you will certainly need to learn more!) refer to the previous Section, and possibly to the CLIPS documentation as mentioned above.

In a typical expert system a fixed set of rules is used, but the fact list changes continuously. However, it is an empirical fact that in most expert systems, much of the fact list is also fairly fixed; although new facts are arriving and old ones being removed at all times, the percentage of facts that change per unit time is generally fairly small. For this reason, the obvious implementation for the expert system shell is a very inefficient one. This obvious implementation would be to keep a list of the rules, and continuously cycle through the list, checking each one's left-hand-side (LHS) against the fact list, and executing the right-hand-side (RHS) of any rules that apply. This is inefficient because most of the tests made on each cycle will have the same results as on the previous iteration; since the fact list is stable, most of the tests will be repeated. You might call this the 'rules finding facts' approach, and the computational complexity is of the order of O(RPF), where R is the number of rules, P is the average number of patterns per rule LHS, and F is the number of facts on the fact list. This is effectively n^2 in the size of the system.

Jess instead uses a very efficient method known as the Rete (Latin for "net") algorithm. The classic paper on the Rete algorithm ("Rete: A Fast Algorithm for the Many Pattern/ Many Object Pattern Match Problem", Charles L. Forgy, Artificial Intelligence 19(1982), 17-37) became the basis for a whole generation of fast expert system shells: OPS5, its descendant ART, and CLIPS. In the Rete algorithm, the inefficiency described above is alleviated (conceptually) by remembering past test results across iterations of the rule loop. Only new facts are tested against any rule LHSs. Additionally, as will be described below, new facts are tested against only the rule LHSs to which they are most likely to be relevant. As a result, the computational complexity per iteration drops to something more like O(sqrt(RP)). Our discussion of the Rete algorithm is necessarily brief; the interested reader is referred to the Forgy paper or to Giarrantano and Riley, "Expert Systems: Principles and Programming", Second Edition, PWS Publishing (Boston, 1993) for a more detailed treatment.

The Rete algorithm is implemented by building a network of nodes, each of which represents one or more tests found on a rule LHS. Facts that are being added to or removed from the fact list are processed by this network of nodes. At the bottom of the network are nodes representing individual rules; when a set of facts filters all the way down to the bottom of the network, it has passed all the tests on the LHS of a particular rule and this set becomes an "activation"; the associated rule may have its RHS executed ("be fired") if the activation is not invalidated first by the removal of one or more facts from its activation set.

Within the network itself there are broadly two kinds of nodes: one-input and two-input nodes. One-input nodes perform tests on individual facts, while two-input nodes perform tests across facts and perform the grouping function. Subtypes of these two classes of node are also used, and there are also auxilliary types such as the terminal nodes mentioned above.

An example is often useful at this point. The following rules:

   (defrule example-2      (defrule example-3
      (x)                     (x)
      (y)                     (y)
      (z)                   => )
     => )
might be compiled into the following network:
        +----+  +----+  +----+     +----+   +----+  (one-input nodes)
        | x? |  | y? |  | z? |     | x? |   | y? |
        +----+  +----+  +----+     +----+   +----+
          \       /       |          \        /
        +------------+    |        +------------+       
        |     +      |    |        |     +      |
        +------------+    |        +------------+   
                  \       |              |          (two-input nodes)
                +------------+           | 
                |     +      |           |
                +------------+           |
                      |                  | 
            +----------------+     +----------------+  
            | fire example-2 |     | fire example-3 |   (terminals)
            +----------------+     +----------------+
The nodes marked x?, etc., test if a fact contains the given data, while the nodes marked + remember all facts and fire whenever they've received data from both their left and right inputs. To run the network, Jess presents new facts to each node at the top of the network as they added to the fact list. Each node takes input from the top and sends its output downwards. A single input node generally receives a fact from above, applies a test to it, and, if the test passes, sends the fact downward to the next node. If the test fails, the one-input nodes simply do nothing. The two-input nodes have to integrate facts from their left and right inputs, and in support of this, their behavior must be more complex. First, note that any facts that reach the top of a two-input node could potentially contribute to an activation: they pass all tests that can be applied to single facts. The two input nodes therefore must remember all facts that are presented to them, and attempt to group facts arriving on their left inputs with facts arriving on their right inputs to make up complete activation sets. A two-input node therefore has a 'left memory' and a 'right memory'. It is here in these memories that the inefficiency described above is avoided. A convenient distinction is to divide the network into two logical components: the single-input nodes comprise the "pattern network", while the two-input nodes make up the "join network".

There are two simple optimizations that can make Rete even better, The first is to share nodes in the pattern network. In the network above, there are five nodes across the top, although only three are distinct. We can modify the network to share these nodes across the two rules (the arrows coming out of the top of the x? and y? nodes are outputs):

                    +--------------------------+
                    ^       +-------------+    |
                    |       ^             |    |
                  +----+  +----+  +----+  |    |  
                  | x? |  | y? |  | z? |  |    |
                  +----+  +----+  +----+  |    | 
                  /      /        /       |    | 
        +------------+  /    +---/   +------------+       
        |     +      |-+    /        |     +      |
        +------------+     /         +------------+   
                  \       /              |     
                +------------+           | 
                |     +      |           |
                +------------+           |
                      |                  | 
            +----------------+     +----------------+  
            | fire example-2 |     | fire example-3 | 
            +----------------+     +----------------+
But that's not all the redundancy in the original network. Now we see that there is one join node that is performing exactly the same function (integrating x,y pairs) in both rules, and we can share that also:
                  +----+  +----+  +----+ 
                  | x? |  | y? |  | z? |
                  +----+  +----+  +----+
                  /      /        / 
        +------------+  /    +---/ 
        |     +      |-+    /     
        +------------+     /     
         |         \       /     
         |       +------------+ 
         |       |     +      |
         |       +------------+
         |             |       
         |   +----------------+  
         |   | fire example-2 |
         |   +----------------+  
   +----------------+  
   | fire example-3 | 
   +----------------+
The pattern and join networks are collectively only half the size they were originally; this kind of sharing comes up very frequently in real systems, and is a significant performance booster!

You can see the amount of sharing in a Jess network by using the 'watch compilations' command. When a rule is compiled and this command has been previously executed, Jess prints a string of characters something like this, which is the actual output from compiling rule example-2, above:

  example-2: +1+1+1+1+1+1+2+2+t
Each time '+1' appears in this string, a new one-input node is created; +2 indicates a new two-input node. Now watch what happens when we compile example-3:
  example-3: =1=1=1=1=2+t
Here we see that =1 is printed whenever a preexisting one-input node is shared; =2 is printed when a two-input node is shared. +t represents the terminal nodes being created. (Note that the number of single-input nodes is larger than expected; Jess creates separate nodes that test for the head of each pattern and its length, rather than doing both of these tests in one node, as we implicitly do in our graphical example.) No new nodes are created for rule example-3; Jess shares existing nodes very efficiently in this case.

Jess's Rete implementation is very literal. Different types of network nodes are represented by various subclasses of the Java class jess.Node: Node1, Node2, NodeNot2, NodeTest, and NodeTerm. The Node1 class is further specialized because it contains a 'command' member which causes it to act differently depending on the tests or functions it needs to perform. For example, there are specializations of Node1 which test the first field (called the 'head') of a fact, test the number of fields of a fact, test single slots within a fact, and compare two slots within a fact. There are further variations which participate in the handling of multifields and multislots. The Jess language code is parsed by the class jess.Jesp, while the actual network is assembled by code in the class jess.ReteCompiler. The execution of the network is handled by the class Rete. The jess.Main class itself is really just a small demonstration driver for the jess package, in which all of the interesting work is done.

The 'view' command, distributed for the first time with Jess 3.2, is a graphical viewer for the Rete network itself; I have used this as a debugging tool for Jess, but it may have educational value for others, and it may help you to design more efficient systems of rules in Jess. Issuing the 'view' command after entering the rules example-2 and example-3 produces a very good facsimile of the drawing (although it correctly shows the larger number of one-input nodes.) The various nodes are color-coded according to their roles in the network; Node1 nodes are red; Node2 nodes are green; NodeNot2 nodes are yellow; and NodeTerm nodes are blue. Passing the mouse over a node displays information about the node and the tests it contains; double-clicking on a node brings up a dialog box containing the same information (for join nodes, the memory contents are also displayed, while for NodeTerm nodes, a pretty-print representation of the the rule is shown.) See the description of the view function for important information before using it.

8 The Future of Jess

Jess will continue to be maintained and improved for the foreseeable future. I have a list of features I plan to implement, but it's hard to associate timescales with any of them. They are listed in no particular order.

9 Version History

Version 4.0
BeanInfo suport; quiz.html embeds only one QuizDisplay applet; Pumps demo works again (sorry); conflict resolution strategy now should be exactly the same as CLIPS' default.
Version 4.0b4
Extensive manual rewrite, adding lots of Java/Jess interoperation info; allow standard CLIPS deffunction docstrings; Thanks to Jack Fitch, Dave Carlson and Alex Jacobson for bean input: property names for reflected Beans now use standard capitalization transform; better error reporting, especially during parsing and from the command line; set and get renamed to set-member and get-member; set and get are now functions that read and write Bean properties; ppdefrule properly handles quoted strings in funcalls; executeCommand and friends reuse a single parser; Thanks for Karl Mueller, Rete.retractString; taught 'batch' to read applet-based data files. 'eval' now handles non-sexps. Better mechanism for synchronizing streams. QuizDisplay is an applet as well as an application. 'run' accepts an argument, the maximum number of rules to fire. Fixed bug in 'modify' when new slot value was a zero-length mf; fixed ReteCompiler bug where MTELN nodes were not consistently generated for zero-length multifield matches. Thanks to Sidney Bailin, fixed problem with accessing defglobals and variables bound to pattern indexes on rule LHSs. Added 'get-var' function. Added undefinstance. Modify and retract now handle definstance facts specially. Fixed some doPPPattern bugs (Dave Carlson again!)
Version 4.0b3
Added jess.reflect package containing new, call, set, and get; added JessListener and its subclasses; added 'engine'. Changed printing of external-addresses to include Java class name. Changed parser to accept variable names as Funcall heads ('call' is substitued, resulting in a runtime error if 'call' is not installed.) 'and' and 'or' functions now accept any values as arguments, not only funcalls. Added 'foreach' control structure. Command prompt doesn't print NIL return values. Fixed another 'not' bug (thanks to Sidney Bailin). Added matching of Java objects on rule LHSs: definstance, defclass. TokenTree now uses sortcode % 101 as hash key, not sortcode itself. All 'global' classes moved into jess package; Jess class renamed 'Main'.
Version 4.0b2
Cleaned up router/parser interactions. Jess will now read only one construct on a line of input (just like CLIPS). All Jess output now goes through WSTDOUT router, not through ReteDisplay.stdout(). Fixed bug whereby second and later references to subfields of multifields on the LHS of a rule would resolve to the whole multifield. 'Modify' can now properly handle multislots. 'Format' handles trailing spaces. Finally, parsing of integers: '2' is a RU.INTEGER, while '2.0' is an RU.FLOAT. Added 'eval' and 'list-function$'.
Version 4.0b1
Code reformat; major performance enhancements (Value and Funcall recycling; Fastfunction interface; Rete memories are now btrees; RU.CLEAR tokens); 'test' CE; return-value constraints; 'ppdefrule' (thanks to Rajaram Ganeshan); blank variables in 'not' CEs; (system) blocks by default; readline fixed; 'build' supported; logic for predicate functions in Rete network now precisely the same as for CLIPS; QuizDisplay demo; while and if accept boolean vars; implied returns from 'if' and 'while' functions; added 'explode$'; added I/O routers: 'open', 'close'. Added 'format'. Added 'bag'.
Version 3.2
system and integer Userfunction classes renamed (Win95 filename capitalization problem!) Broken delete$, insert$, replace$ fixed. 'view' command added. Big if/then in Funcall class finally removed in favor of separate implementation classes for intrinsics, leading to a modest speed increase. Documentation vastly expanded! Added catch for ArrayOutOfBoundsException in command-line interface; no more crash on wrong number of args. Broken evenp, oddp fixed. str-cat, sym-cat made more general. Broken sub-string fixed. Big switch in Node1 class replaced by separate classes, leading to a very modest speed increase.
Version 3.1:
Added the 'assert-string' and 'batch' commands. Two bug fixes in multislot code (thanks to Nancy Flaherty). Added 'undefrule' and the ability to redefine rules. Added the 'system' function, although it doesn't work very well under Java. Public function engine() in jess.Context class allows you to do fancier things in UserFunctions. Added the non-standard 'load-package' and 'load-function' functions. Many new contributed functions packaged with Jess for doing math, handling multifields, and other neat stuff; thanks to Win Carus for these. Added 'time' (1 second resolution).
Version 3.0:
A few code changes to accomodate Microsoft's Java compiler; Jess now compiles unchanged with JVC (thanks to Mike Finnegan.) Added 'member$' multifield function. Added 'clear' intrinsic (thanks to Karl Mueller.) Introduced a new way of handling (not) patterns which I think finally guarantees there are no more not-related bugs remaining! 'load-facts', which has been non-functional throughout the beta period, is working again. Documentation now explains unzipping and compiling a little better. Modified the way fact-id's are handled so that you can write '(retract 3)' to retract fact #3.
Version 3.0b2:
LOTS of bug reports and improvement suggestions from the field - thanks folks! All the reported bugs in the multifield implementation, and some residual odd behavior in the "not" CE, have been fixed. The (exit) command has been added. A command prompt has been added. The '#' character can now be used in symbols. The access levels on some methods in the Rete class have been opened up; Rete is no longer final. nth$ is now 1-based, as it is in CLIPS. The "if" and "while" constructs now fire on 'not FALSE' instead of 'TRUE'. The str-index function has been fixed and added. Probably a few more things I'm forgetting here. Thanks for the input; particular thanks to Nancy Flaherty, Joszef Toth, Karl Mueller, Duane Steward, and Michelle Dunn for reporting bugs fixed in this version; sorry if I left anyone out.
Version 3.0b1:
First public release of Jess 3.0.
Version 3.0a3:
UserPackage interface; lots of new example UserFunctions for multifields, string, and predicates.
Version 3.0a2:
Multislots! Also important bug fix: under certain circumstances, the Rete network compilation could fail 1) if (not ()) CEs occurred on the LHS of a rule, 2) new variables were introduced in that rule's patterns listed after the (not ()) CEs, and 3) these latter variables were tested (i.e., in a predicate constraint) on the LHS of the rule.
Version 3.0a1:
Incremental reset. Watch activations. gc() in LostDisplay, NullDisplay. Multifields! All the Rete engine classes are now in a package named 'jess'. Many classes and methods that should not be manipulated by clients are now package-private.
Version 2.2.1:
Ken Bertapelle found another bug, which has been squashed, in the pattern network.
Version 2.2:
Jess 2.2 adds a few new function calls (load-facts, save-facts) and fixes a serious bug (thanks to Ken Bertapelle for pointing it out!) which caused Jess to crash when predicate constraints were used in a certain way. Another bugfix corrected the fact that 'retract' only retracted the first of a list of facts. Jess used to give a truly inscrutable error message if a variable was first used in a not CE (a syntax error); the current error message is much easier to understand. I also clarified a few points in the documentation.
Version 2.1:
Jess 2.1 is *much* faster than version 2.0. The Monkey example runs in about half the time as under Jess 2.0, and for some inputs, the speed has increased by an order of magnitude! This is probably the last big speed increase I'll get. For Java/Rete weenies, this speed increase came from banishing the use of java.lang.Vector in Tokens and in two-input node memories. Jess is now within a believeable interpreted Java/C++ speed ratio range of about 30:1. Jess 2.1 now includes rule salience. It also implements a few additional intrinsic functions: gensym*, mod, readline. Jess 2.1 fixes a bug in the way predicate constraints were parsed under some conditions by Jess 2.0. The parser now reports line numbers when it encounters an error.
Version 2.0:
Jess 2.0 is intrinsically about 30% faster than version 1.0. The internal data structures changed quite a bit. The Rete network now shares nodes in the Join network instead of just in the pattern network. The current data structures should allow for continued improvement.
If anyone writes an emulation of a CLIPS function that Jess omits, please send it to me and I'll include it in the next release (with credit to you, of course.)

At the time of this writing, Jess has more than 5000 registered users. I have been very pleased by this response and have enjoyed working with many of Jess's more ambitious users. If you use Jess, and if you have comments, questions, or concerns, please don't hesitate to ask.

Finally, thanks to Gary Riley and the gang at NASA for writing the marvelous CLIPS in the first place!

Ernest Friedman-Hill                        Phone: (510) 294-2154
Distributed Computing                       FAX:   (510) 294-2234
Sandia National Labs                        ejfried@ca.sandia.gov
Org. 8920, MS 9214                  http://herzberg.ca.sandia.gov
PO Box 969
Livermore, CA 94550