CLIPS User's Guide

Chapter 10 Fascinating Facets

If you want to have class, then act, dress, and talk like your friends.

In this chapter you'll learn more about slots and how to specify their characteristics by using facets. The use of facets is good software engineering because there is a greater chance of CLIPS flagging an illegal value rather than risking a runtime error or crash. There are many types of facets that may be used to specify slots, as summarized in the following table.

Facet Name Description

default and default-dynamic Set initial values for slots
cardinality Number of multifield values
access type Read-write, read-only, initialize-only access
storage Local slot in instance or shared slot in class
propagation Inherit, or no inherit slots
source composite or exclusive inheritance
documentation Documentation of slots
overridemessage Indicate message to send for slot override
create- accessor Create put- and get- handlers
visibility Public, or private to defining class only
reactive Changes to a slot trigger pattern- matching

For reasons of space, we'll only describe a few facets in more detail in the rest of this chapter. For more details, see the CLIPS Reference Manual.

A Slot Named DefaultThe default facet sets the default value of a slot when an instance is created or initialized, as shown in the following example.

CLIPS> (clear)

CLIPS> (defclass DUCK (is-a USER)(role concrete)

(slot sound (create-accessor read-write)(default quack))

(slot ID (create-accessor read-write))

(slot sex (create-accessor read-write)(default male)))

CLIPS> (make-instance Dorky_Duck of DUCK)

[Dorky_Duck]

CLIPS> (send [Dorky_Duck] print)

[Dorky_Duck] of DUCK

(sound quack)

(ID nil)

(sex male)

CLIPS>

As you can see, the default values for slot sex and slot sound were set by the default facet values. Following the default keyword can be any valid CLIPS expression that does not involve a variable. For example, the default expression of the sound slot is the symbol quack. Functions may be used in the facet expression as will be shown in the next example.

This default facet is a static default because the value of its facet expression is determined when the class is defined and never changed unless the class is redefined. For example, let's set the default value of slot ID to the (gensym*) function which returns a new value not in the system everytime it's called.

CLIPS> (clear)

CLIPS> (defclass DUCK (is-a USER)(role concrete)

(slot sound (create-accessor read-write)(default quack))

(slot ID (create-accessor read-write)(default (gensym*)))

(slot sex (create-accessor read-write)(default male)))

CLIPS> (make-instance [Dorky_Duck] of DUCK) ; Dorky_Duck #1

[Dorky_Duck]

CLIPS> (send [Dorky_Duck] print)

[Dorky_Duck] of DUCK

(sound quack)

(ID gen1)

(sex male)

CLIPS> (make-instance [Dorky_Duck] of DUCK) ; Dorky_Duck #2

[Dorky_Duck]

CLIPS> (send [Dorky_Duck] print)

[Dorky_Duck] of DUCK

(sound quack)

(ID gen1)

(sex male)

CLIPS>

As you can see, the ID is always gen1 since (gensym*) is only evaluated once, and not again when the second instance is created. Note that the (gensym*) values may be different on your computer if you have already called the (gensym*) since it increments by one each time it is called, and is not reset by a (clear). The (gensym*) function is reset to its starting value only if you restart CLIPS.

Now suppose that we want to keep track of the different Dorky_Duck instances that have been created. Rather than using the static default, we can use the facet called default dynamic which will evaluate its facet expression every time a new instance is created. Notice the difference between the following example and the previous.

CLIPS> (clear)

CLIPS> (defclass DUCK (is-a USER)(role concrete)

(slot sound (create-accessor read-write)(default quack))

(slot ID (create-accessor read-write)

(default-dynamic (gensym*)))

(slot sex (create-accessor read-write)(default male)))

CLIPS> (make-instance [Dorky_Duck] of DUCK) ; Dorky_Duck #1

[Dorky_Duck]

CLIPS> (send [Dorky_Duck] print)

[Dorky_Duck] of DUCK

(sound quack)

(ID gen2)

(sex male)

CLIPS> (make-instance [Dorky_Duck] of DUCK) ; Dorky_Duck #2

[Dorky_Duck]

CLIPS> (send [Dorky_Duck] print)

[Dorky_Duck] of DUCK

(sound quack)

(ID gen3) ; Note ID is different from Dorky_Duck #1

(sex male)

CLIPS>

In this example which uses dynamic default, the ID of the second instance, gen3, is different from the first instance, gen2. In contrast, for the previous example of static default, the ID values were the same, gen1, since the (gensym*) was only evaluated once when the class was defined rather then for every new instance in the case of dynamic default.

Cardinal PropertiesThe cardinality of a slot refers to one of two types of fields that a slot can hold: (1) single-field, or (2) multifield. The term cardinality refers to a count. A bound single- field slot contains only one field, while a bound multifield slot may contain zero or more fields. The bound single-field slot and the bound multifield slot each contain one value. However, the one multifield value may have multiple fields in it. For example, (a b c) is a single multifield value with three fields. The empty string "" is a single-field value, just as "a b c" is. In contrast, an unbound slot has no value.

As an analogy to single and multifield variables, think of a slot as your mailbox. Sometimes you may find a single piece of junk-mail that doesn't have an envelope. Instead, an address label has just been stuck on a folded piece of paper addressed to "Resident." Other times you may find an envelope with multiple ads in it. The single piece of junk mail without an envelope is like a single-field value while the envelope with multiple ads is like the multifield value. If the junk-mail distributor slips up and mails you an envelope with nothing inside, this corresponds to the empty multifield variable. (Come to think of it, if the junk-mail envelope is empty, have you really received junk-mail?)

A multiple facet with keyword multislot, is used to store a multifield value as shown in the following example.

CLIPS> (clear)

CLIPS> (defclass DUCK (is-a USER)(role concrete)

(multislot sound (create-accessor read-write)

(default quack quack)))

CLIPS> (make-instance [Dorky_Duck] of DUCK)

[Dorky_Duck]

CLIPS> (send [Dorky_Duck] print)

[Dorky_Duck] of DUCK

(sound quack quack)

CLIPS>

A multifield value can be accessed using get- and put- as shown in the following examples, which shows how to keep track of quacks.

CLIPS> (send [Dorky_Duck] put-sound quack1 quack2 quack3)

(quack1 quack2 quack3)

CLIPS> (send [Dorky_Duck] get-sound)

(quack1 quack2 quack3)

CLIPS>

Standard CLIPS functions such as nth$ to get the nth field of a multislot value can be used. The following example shows how to pick a certain quack.

CLIPS> (nth$ 1 (send [Dorky_Duck] get-sound))

quack1

CLIPS> (nth$ 2 (send [Dorky_Duck] get-sound))

quack2

CLIPS> (nth$ 3 (send [Dorky_Duck] get-sound))

quack3

CLIPS>

Other Features

CLIPS has several multifield slot functions as shown in the following table.

Function Meaning

slot-replace$ Replace the specified range
slot-insert$ Insert the specified range
slot-delete$ Delete the specified range

A multifield slot with no values, e.g., the empty multifield value (), may be assigned to a slot with a (multiple) facet. Note that there is a difference between a slot with an empty multifield value () and an unbound slot. If you think of an empty multifield value as analogous to an empty bus, you can see there is a difference between no people (unbound slot) and a bus with no people (empty multifield value, () ).

A storage facet defines one of two places that a slot value is stored: (1) in an instance, or (2) in the class. A slot value stored in the instance is called local because the value is only known to the instance. Thus, different instances may exist that have different local slot values. In contrast, a slot value stored in a class is called shared because it is the same for all instances of the class.

A local value is specified by the local facet, which is the default for a slot. A shared value is specified by the shared facet and all instances with this slot type will have their slot value automatically changed if one changes. An access facet defines one of three types of access for a slot, as summarized in the following table.

Another way to set the instance values is with the initialize-instance function. An (initialize-instance) can be called at any time to reset the default values and retain values in non-(default) slots.

A (reset) can be thought of as a coldinitialization since all values in non- (default) slots are cleared, while a (initialize- instance) can be considered a warminitialization since non-(default) values are retained. Of course, only (definstances) can be cold- initialized since non-(definstances) will simply be deleted. Also, slot- overrides may be used in (initialize-instance) as the last example shows.

Two predicate functions are designed for use with the access facets. Both these predicate functions return an error message if the specified slot or instance does not exist. The slot-writablep is a predicate function which returns TRUE if a slot is writable and FALSE if it is not. The slot-initablep predicate function returns TRUE if the slot is initializable and FALSE if it is not. The term initializable means that it is not read-only.

The inherit facet, which is the default, specifies that indirect instances of a class may inherit this slot from the class. As you recall, the indirect instances of a class are the instances of its subclasses, while the direct instances are those defined specifically for the class. The indirect instances of a class are direct instances of the subclasses in which they are defined. For example, [Dorky_Duck] is a direct instance of DUCK and an indirect instance of USER which is the superclass of DUCK. The no-inherit facet specifies that only a direct instance has the class slot.

It's important to realize that the (no-inherit) facet only prohibits inheritance from the class and not from its superclasses. This means that an instance may still inherit from superclasses of the (no-inherit) class.

The composite facet. facet states that facets which are not explicitly defined in the highest precedence class take their facets from the next higher precedence class. If the facet is not explicitly set in the next higher precedence class and it is composite too, CLIPS tries the next higher and so on until the facet is defined or there are no more classes. If the next higher class is not composite, CLIPS does not check further. The opposite to the composite facet is the exclusive facet, which is the default. For more information, see the CLIPS Reference Manual.