Jess Information

Jess Home
Jess 7 Features
Download Now!
Online Demo

Mailing List
Jess Wiki

More information Related Web Sites
User Contributions
JSR94 Info
Developer's Log
About This Site

JESS ®, the Rule Engine for the JavaTM Platform

Jess Wiki: Facts Vs Shadow Facts

You should already know that there are three basic types of facts in Jess:

  • Ordered facts
  • Unordered facts
  • Shadow Facts

Ordered and unordered facts are 100% pure Jess. Shadow facts, however, are backed by a Java POJO bean that holds the same data as the fact. As a result, they have some fundamental differences:

  • In Jess 6, the templates for shadow facts are defined using defclass, rather than deftemplate (in Jess 7, you can use deftemplate for both.)
  • The Java classes that will be used for a dynamic shadow facts should fire property change events in all of their set methods.
  • Shadow facts always have slot values for “class”, which is the instance of java.lang.Class representing the class of the backed Java object, and “OBJECT”, which is a reference to the backed Java object itself.
  • The reset function will not remove shadow facts from working memory. They must be retracted explicitly.
  • Shadow facts may be either static or dynamic.
    • Static shadow facts will have their slots updated from the backed Java object during a reset.
    • Dynamic shadow facts will use property change listeners to dynamically update their slot values as the Java object changes.
  • Shadow facts cannot be asserted. Instead, the backed Java object must first be constructed and then added to working memory as a fact using definstance.
  • Shadow facts cannot be defined using a deffacts.
  • The duplicate function cannot be used on shadow facts

Shadow facts are extremely useful for representing data that needs to be shared between both the Java and Jess environments of your program. There are some pitfalls to using them, however:

Reset Doesn’t Remove Shadow Facts!

This is clearly stated in the Jess manual, JIA, and in my list above, but still it seems to surprise people when they first encounter it in their Jess program. A definstance acts like a deffacts. When you call reset, your shadow facts will reinitialize themselves from the backed Java objects. They don’t leave working memory, i.e. your fact list, unless you explicitly retract them.

Populate the Java Object First!

The backed Java object should be fully populated before adding the shadow fact to working memory by calling definstance. If you call definstance first and then try to set all the slot values in your fact, you will cause additional, unnecessary pattern-matching cycles in the Rete algorithm, resulting in slower performance.

Shadow Facts are Usually Unique!

As I showed in the FactsVsObjects section, you can’t assert two facts that are exactly identical. This rule starts to break down when you start working with shadow facts. The reason for this is the "OBJECT" slot. Take our coordinate fact example from FactsVsObjects. Assume we do a defclass on the Coordinate class:

    (defclass coordinate Coordinate)
Now, let’s look at the template derived from the Coordinate class:
    Jess> (ppdeftemplate coordinate)
    "(deftemplate MAIN::coordinate
       \"$JAVA-OBJECT$ Coordinate\" 
       (declare (from-class Coordinate)) 
       (slot class) 
       (slot x) 
       (slot y) 
       (slot OBJECT))"

Now, we assert two new coordinates facts, both at X = 2.0 and Y = 5.0, using definstance rather than assert since they’re now shadow facts:

    Jess> (definstance coordinate (new Coordinate 2.0 5.0))
    Jess> (definstance coordinate (new Coordinate 2.0 5.0))
This time, both facts were asserted successfully. Looking in our fact list, we see:
    Jess> (facts)
    f-0   (MAIN::initial-fact)
    f-1   (MAIN::coordinate (class <Java-Object:java.lang.Class>) (x 2.0) (y 5.0) (OBJECT <Java-Object:Coordinate>))
    f-2   (MAIN::coordinate (class <Java-Object:java.lang.Class>) (x 2.0) (y 5.0) (OBJECT <Java-Object:Coordinate>))
Why didn’t the second definstance fail? Simple: the facts are not the same. They’re referring to different Java objects in the "OBJECT"; hence they’re different.

Now try asserting two coordinate facts that refer to the same Java object:

    Jess> (bind ?coordinate (new Coordinate 2.0 3.0))
    Jess> (definstance coordinate ?coordinate)
    Jess> (definstance coordinate ?coordinate)
It isn’t as obvious as the FALSE return value in the FactsVsObjects example, but the "<Fact--1>" return value indicates that the fact already exists in working memory and was not re-asserted. We can see this by looking at working memory:
    Jess> (facts)
    f-0   (MAIN::initial-fact)
    f-1   (MAIN::coordinate (class <Java-Object:java.lang.Class>) (x 2.0) (y 3.0) (OBJECT <Java-Object:Coordinate>))
    For a total of 2 facts in module MAIN.

Submitted by:
Union Pacific Railroad

FrontPage, JavaPitfalls

Front Page | Sandbox | Recent Changes | Powered by Friki | Last Edited: 18 August 2006