5. Working Memory

Jess maintains a collection of knowledge nuggets called facts. This collection is known as the working memory. The most important thing to know about working memory is that rules can only react to additions, deletions, and changes to working memory. You can't write a Jess rule that will react to anything else. Working memory is therefore very important.

The good news is that working memory can contain Java objects, so that you can write rules that react to them. Each such Java object will appear to be represented by a single fact, Still, your rules will only know about those Java objects that have been placed in working memory. This section of the manual explains how to do that.

Working memory is something like a relational database, especially in that the facts must have a specific structure. From Jess's perspective, every fact in working memory looks like a row in a relational table. It has a name, like the name of the relation, and it has a set of properties, like the columns. This is the same structure that JavaBeans -- plain old Java objects, or POJOs -- have. You can think of it either way. Every fact corresponds to a single instance of the jess.Fact class.

In Jess, there are three kinds of facts: unordered facts, definstance facts (which are really just a kind of unordered facts) and ordered facts. You'll learn about unordered and ordered facts in this chapter, and about definstance facts here.

5.1. Unordered facts

In object-oriented languages, objects have named fields in which data appears. Unordered facts offer this capability (although the fields are traditionally called slots.)
(person (name "Bob Smith") (age 34) (gender Male))
(automobile (make Ford) (model Explorer) (year 1999))
before you can create unordered facts, you have to define the slots they have using the deftemplate construct:
(deftemplate <deftemplate-name> [extends <classname>] [<doc-comment>]
  [(declare [(<declarand> <value>)]*)]
  [(slot <slot-name> [(default | default-dynamic <value>)]
                           [(type <typespec>))]*)
The <deftemplate-name> is the head of the facts that will be created using this template. There may be an arbitrary number of slots. Each <slot-name> must be a symbol. The default slot qualifier states that the default value of a slot in a new fact is given by <value>; the default is the symbol nil. The 'default-dynamic' version will evaluate the given value each time a new fact using this template is asserted. The 'type' slot qualifier is accepted but not currently enforced by Jess; it specifies what data type the slot is allowed to hold. Acceptable values are ANY, INTEGER, FLOAT, NUMBER, SYMBOL, STRING, LEXEME, and OBJECT.

As an example, defining the following template:

Jess> (deftemplate automobile
  "A specific car."
  (slot make)
  (slot model)
  (slot year (type INTEGER))
  (slot color (default white)))
would allow you to define facts as shown here. Note that you can see a list of all the facts in the working memory using the facts command.

Jess> (reset)
(assert (automobile (make Chrysler) (model LeBaron)
              (year 1997)))
Jess> (facts)

f-0   (MAIN::initial-fact)
f-1   (MAIN::automobile (make Chrysler) (model LeBaron)
                        (year 1997) (color white))
For a total of 2 facts in module MAIN.

Note that the car is white by default. If you don't supply a default value for a slot, and then don't supply a value when a fact is asserted, the special value nil is used. Also note that any number of additional automobiles could also be simultaneously asserted onto the fact list using this template.

As you can see above, each fact is assigned an integer index (the fact-id) when it is asserted. You can remove an individual fact from the working memory using the retract function.
Jess> (retract 1)
                Jess> (facts)
                f-0   (MAIN::initial-fact)
                    For a total of 1 facts in module MAIN.

The fact (initial-fact) is asserted by the reset command. It is used internally by Jess to keep track of its own operations; you should generally not retract it.

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:

Jess> (deftemplate box (slot location) (multislot contents))
(bind ?id (assert (box (location kitchen)
                             (contents spatula sponge frying-pan))))

(We're saving the fact returned by (assert) in the variable ?id, for use below.) A multislot has the default value () (the empty list) if no other default is specified.

You can change the values in the slots of an unordered fact using the modify command. Building on the immediately preceding example, we can move the box into the dining room:

Jess> (modify ?id (location dining-room))
Jess> (facts)
f-0   (MAIN::initial-fact)
f-2   (MAIN::box (location dining-room)
                 (contents spatula sponge frying-pan))
For a total of 2 facts in module MAIN.

The optional extends clause of the deftemplate construct lets you define one template in terms of another. For example, you could define a used-auto as a kind of automobile with more data:

Jess> (deftemplate used-auto extends automobile
  (slot mileage)
  (slot blue-book-value)
  (multislot owners))

A used-auto fact would now have all the slots of an automobile, plus three more. As we'll see later, this inheritance relationship will let you act on all automobiles (used or not) when you so desire, or only on the used ones.

You can completely clear Jess of all facts and other data using the clear command.

5.1.1. The slot-specific declaration

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

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

Without the "slot-specific" declaration, this rule would enter an endless loop, because it modifies a fact matched on the LHS in such a way that the modified fact will still match. With the declaration, it can simply fire once. This behavior is actually what many new users expect as the default, so the slot-specific declaration probably ought to be used most of the time.

5.2. Ordered facts

Most of the time, you will use unordered facts (or their cousins, definstance facts. They are nicely structured, and they're the most efficient kind of fact in Jess. In some cases, though, slot names are redundant, and force you to do more typing than you'd like. For example, if a fact represents a single number, it seems silly to use an unordered fact like this:

(number (value 6))

What you'd like would be a way to leave out that redundant "value" identifier. Ordered facts let you do exactly that.

Ordered facts are simply Jess lists, where the first field (the head of the list) acts as a sort of category for the fact. Here are some examples of ordered facts:
            (shopping-list eggs milk bread)
            (person "Bob Smith" Male 35)
            (father-of danielle ejfried)
You can add ordered facts to the working memory using the assert function, just as with unordered facts. If you add a fact to working memory whose head hasn't been used before, Jess assumes you want it to be an ordered fact. Alternatively, you can explicitly declare an ordered template using the deftemplate construct like this:
Jess> (deftemplate father-of
                "A directed association between a father and a child."
                (declare (ordered TRUE)))
The quoted string is a documentation comment; you can use it to describe the template you're defining. Although declaring ordered templates this way is optional, it's good style to declare all your templates.

Note that an ordered fact is very similar to an unordered fact with only one multislot. The similarity is so strong, that in fact this is how ordered facts are implemented in Jess. If you assert an ordered fact, Jess automatically generates a template for it. This generated template will contain a single slot named "__data". Jess treats these facts specially - the name of the slot is normally hidden when the facts are displayed. This is really just a syntactic shorthand, though; ordered facts really are just unordered facts with a single multislot named "__data".

5.3. The deffacts construct

Typing separate assert commands for each of many facts is rather tedious. To make life easier in this regard, Jess includes the deffacts construct. A deffacts construct is a simply a named list of facts. The facts in all defined deffacts are asserted into the working memory whenever a reset command is issued:

Jess> (deffacts my-facts "The documentation string"
  (foo bar)
  (box (location garage) (contents scissors paper rock))
  (used-auto (year 1992) (make Saturn) (model SL1)
             (mileage 120000) (blue-book-value 3500)
             (owners ejfried)))
Jess> (reset)
Jess> (facts)
f-0   (MAIN::initial-fact)
f-1   (MAIN::foo bar)
f-2   (MAIN::box (location garage) (contents scissors paper rock))
f-3   (MAIN::used-auto (make Saturn) (model SL1) (year 1992)
                 (color white) (mileage 120000)
                 (blue-book-value 3500) (owners ejfried))
For a total of 4 facts in module MAIN.

Note that we can specify the slots of an unordered fact in any order (hence the name.) Jess rearranges our inputs into a canonical order so that they're always the same.