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 As Slot Values


Wolfgang Laun
Wolfgang Laun a-t gmail com

Name: Facts as slot values


Currently the fact-saving function save-facts and save-facts-xml cannot be used for facts using references to other facts as slot values.


Simplify templates, rules and patterns by using fact references as slot values.


Using a reference to a fact as a slot value simplifies patterns and the processing on a rule's right hand side. It may also save you the trouble of inventing a unique identifier for distinguishing potentially identical facts.

If a slot value containing a fact reference is bound to a variable, you may access its slots with a call to (fact-slot-value...). Another approach would be the definition of individual getters and setters, and this task can even be automated. (See AsBeanUserFunction.)

Facts that are used for triggering some operation on the set of "steady" facts (cf. the Locator facts in the sample code) may, of course, still have to use some slot value (or slot value combination) to locate their target or targets. But once a fact has been located, its reference may be kept for straightforward access.


This technique may be advantageuos whenever there are stable facts, related somehow to each other. (References to facts that are retracted ´shouldn't be used in this way.)


Some Jess command extensions are used in the sample code. See AddingCommandsToJess: InterpolateUserFunction, AsBeanUserFunction and GetTemplateUserFunction.

Sample code:

Notice that the sample code refers to a file royals.txt which isn't included here. It might be used to run calls like these:

Jess> (getChildren "George III")
Augustus Duke of Sussex is a child of George III.
Ernest Duke of Cumberland is a child of George III.
Edward Duke of Kent is a child of George III.
William IV is a child of George III.
Frederick Duke of York is a child of George III.
George IV is a child of George III.

Jess> (getParents  "George II")
George II is the child of George I and Sophia Dorothea of Brunswick-Lüneberg.

Jess> (getSiblings "Charles II")
James II is a sibling of Charles II.
Mary is a sibling of Charles II.
Henrietta is a sibling of Charles II.

Jess> (getParents  "Henry III")
Henry III is the child of John Lackland and Isabella of Angoulęme.

Jess> (getSuccessor "James II")
King William III is the successor of King James II.
Queen Mary II is the successor of King James II.
If you are interested in the English royals' family tree, contact the author.

The first section of the Jess script defines some templates. Notice that extending a template into several subtemplates, without adding slots to any of them, is a nice way of creating an additional attribute.

;; Jess for historians: research on a royal family tree.

(load-function at.laune.jess.Interpolate)
(load-function at.laune.jess.AsBean)
(load-function at.laune.jess.GetTemplate)

(deftemplate Person
    (slot name   (type STRING))
    (slot nick   (type STRING))
    (slot title  (type STRING))
    (slot f      (type OBJECT))    ; father
    (slot m      (type OBJECT)))   ; mother

(deftemplate Lord extends Person)
(deftemplate Lady extends Person)

(deftemplate Sovereign extends Person
    (slot from (type INTEGER))
    (slot to   (type INTEGER)))

(as-bean Sovereign)

(deftemplate King  extends Sovereign)
(deftemplate Queen extends Sovereign)

(deftemplate Locator
    (slot func (type SYMBOL))
    (slot name (type STRING)))

(deftemplate Marker
    (slot person  (type OBJECT))
    (slot primary (type OBJECT)))

(deffunction nickname (?p)
    (bind ?name (getName ?p))
    (bind ?nick (getNick ?p))
    (return (if (neq ?nick nil) then (ip "?name ?nick") else ?name))

(deffunction getBase (?p)
    (bind ?temp (get-template ?p))
    (bind ?pos (+ (str-index "::" ?temp) 2))
    (return (sub-string ?pos (str-length ?temp) ?temp ))

The next lines create the "stable" facts. We cannot register the parents for all of our nobility. But, rather than leaving the slots set to nil, we store a reference to a generic dummy, and this dummy points back to itself. (This absurd parentage is useful since it avoids checking for nil in lots of places.)


;; Build the "undef" person, and link its parents to itself.
(defglobal ?*undef* = (assert (Person (name "<undef>"))))
(setF ?*undef* ?*undef*)
(setM ?*undef* ?*undef*)

;; Create some royals.
(batch royals.txt)

To simplify patterns testing for ancestors, the dummy person is entered wherever there is no information about a parent.

(defquery allPersons ?p <- (Person))

(bind ?res (run-query* allPersons))
(while (?res next)
    (bind ?person (?res get p))
    (if (eq (getF ?person) nil) then
        (modify ?person (f ?*undef*))
    (if (eq (getM ?person) nil) then
        (modify ?person (m ?*undef*))

Rules childrenOf, parentsOf and siblingsOf locate the corresponding relatives of some person in our family tree.

;; Find a person's children.
(defrule childrenOf
    ?loc    <- (Locator (func sParent)(name ?name))
    ?parent <- (Person (name ?name))
    (or ?child <- (Person (f ?parent))
        ?child <- (Person (m ?parent)))
    (bind ?cname (nickname ?child))
    (bind ?pname (nickname ?parent))
    (printout t (ip "?cname is a child of ?pname.") crlf)

;; Find a person's parents.
(defrule parentsOf
    ?loc   <- (Locator (func sChild)(name ?name))
    ?child <- (Person (name ?name)(f ?father)(m ?mother))
    (bind ?fname (nickname ?father))
    (bind ?mname (nickname ?mother))
    (printout t (ip "?name is the child of ?fname and ?mname.") crlf)

;; Find a person's siblings.
(defrule siblingsOf
    ?loc <- (Locator (func sSibling)(name ?name))
    ?p   <- (Person (name ?name) (f ?f1 &:(neq ?f1 ?*undef*))
                                 (m ?m1 &:(neq ?m1 ?*undef*)))
    ?s   <- (Person {f == ?f1} {m == ?m1})
    (test (neq ?s ?p))
    (bind ?np (nickname ?p))
    (bind ?ns (nickname ?s))
    (printout t (ip "?ns is a sibling of ?np.") crlf)
A sovereign's successor is the next succeeding royalty. This is (at least in England's history) complicated by the possibility that two persons, with equal rights, may meet the definition. (King James II was followed by King William III and his spouse, Queen Mary.)
;; Find a sovereign's successor.
;; Stage 1: place markers.
(defmodule SUCC1)
(defrule succOfFanOut
    (Locator (func sSucc)(name ?name))
    ?pred <- (Sovereign (name ?name)(to ?year))
    ?cand <- (Sovereign (name ?cname)(from ?from &:(>= ?from ?year)))
    (assert (Marker (person ?cand)(primary ?pred)))

;; Stage 2: find earliest possible successor(s).
(defmodule SUCC2)
(defrule succOfZoomIn
    ?m1 <- (Marker (person ?pers1))
    ?m2 <- (Marker (person ?pers2 &~ ?pers1))
    (bind ?y1 (getFrom ?pers1))
    (bind ?y2 (getFrom ?pers2))
    (if (< ?y1 ?y2) then
        (retract ?m2)
    (if (< ?y2 ?y1) then
        (retract ?m1)

;; Stage 3: display result.
(defmodule SUCC3)
(defrule succOfShow
    ?mrk <- (Marker (person ?succ)(primary ?pred))
    (bind ?pname (nickname ?pred))
    (bind ?pwhat (getBase ?pred))
    (bind ?sname (nickname ?succ))
    (bind ?swhat (getBase ?succ))
    (printout t (ip "?swhat ?sname is the successor of ?pwhat ?pname.") crlf)
    (retract ?mrk)

;; Cleanup: remove the Locator fact.
(defmodule CLEAN)

(defrule cleanUp 
   ?l <- (Locator)
    (retract ?l)
Finally, here are some functions for triggering the rules, and some examples of how to call them.
(deffunction getChildren (?name)
    (assert (Locator (func sParent) (name ?name)))
    (focus CLEAN)

(deffunction getParents (?name)
    (assert (Locator (func sChild) (name ?name)))
    (focus CLEAN)

(deffunction getSiblings (?name)
    (assert (Locator (func sSibling) (name ?name)))
    (focus CLEAN)

(deffunction getSuccessor (?name)
    (assert (Locator (func sSucc)   (name ?name)))
    (focus SUCC1 SUCC2 SUCC3 CLEAN)

Front Page | Sandbox | Recent Changes | Powered by Friki | Last Edited: 06 July 2007