copyright © 1982, by the author(s). all rights reserved...

87
Copyright © 1982, by the author(s). All rights reserved. Permission to make digital or hard copies of all or part of this work for personal or classroom use is granted without fee provided that copies are not made or distributed for profit or commercial advantage and that copies bear this notice and the full citation on the first page. To copy otherwise, to republish, to post on servers or to redistribute to lists, requires prior specific permission.

Upload: others

Post on 15-Sep-2019

2 views

Category:

Documents


0 download

TRANSCRIPT

Copyright © 1982, by the author(s). All rights reserved.

Permission to make digital or hard copies of all or part of this work for personal or

classroom use is granted without fee provided that copies are not made or distributed for profit or commercial advantage and that copies bear this notice and the full citation

on the first page. To copy otherwise, to republish, to post on servers or to redistribute to lists, requires prior specific permission.

USING THE PEARL AI PACKAGE

(Package for Efficient Access to Representation in Lisp)

by

M. Deering, J. Faletti and R. Wilensky

Memorandum No. UCB/ERL M82/19

31 March 1982

(Revised 27 May 1983)

Using the PEARL AI Package

(Package for /Efficient Access toRepresentations in lisp)*

Michael DeeringJoseph Faletti

Robert Wilensky

Computer Science DivisionDepartment of EECS

University of California. BerkeleyBerkeley, California 94720

February 19B2

ABSTRACT

This document is a tutorial and manual for PEARL(Package for Efficient Access to Representations in Lisp),an AI language developed with space and time efficienciesin mind. PEARL provides a set of functions for creatinghierarchically-defined slot-filler representations and forefficiently and flexibly inserting and fetching them from aforest of associative data bases. In addition to providingthe usual facilities such as demons and matching, PEARLintroduces stronger typing on slots and user-assistedhashing mechanisms.

♦ This research was sponsored in part by the Office of Naval Itesearchi^er contractN00014-80-C-0732 and the National Science Foundation under grant HCS7B-08543.

Table of Contents

1. Introduction *2. Running PEARL «

2.1. Under FY-anz lisp *^2. Under UCI lisp 3

3. Creating Simple Objects *3.1. Defining Symbols *3.2. Defining Structures jj

4. Creating Individual Instances of Defined Structures 65. Accessing Slots of Structures 86. Storing In and Retrieving From the Data Base - The Simplest Way 11

6.1 Storing In the Data Base: Insertdb andRemovedb 116.2 Retrieving Hash Buckets From the Data Base: Fetch 126.3 Accessing the Results of Fetch: Nextitem 13

7. The DefaultValues for Unspecified Slots 14B. Using Patterns For More Flexible and Powerful Retrieval 159. Marking Structures During Creation For More Efficient Retrieval 1710. Printing Structures, Symbols and Other PEARL Objects 2011. Error Messages, Bugs, and Error Handling Abilities 2112. Short-Circuiting andRedirecting Create Using !, I andAtoms 2213. More Flexible Hash Selection 2314. Using Predicates to Constrain Fetching 2715. More Useful Slot Types 3016. Attaching Hooks to Structures (If-Added Demons) 3217. Creating and Manipulating Multiple Data Bases 38IB. Creating a Forest of Data Bases 3919. Creating Expanded Subtypes of Previously Defined Objects 4020. Fetching Expanded Structures 4221. How Two Objects Match 43

21.1 When Is a Pattern not a Pattern? 4321.2 The Matching Process 44

22. Binding Blocks of Structures Together Via Common Variables 4723. Controlling the Unbinding of Variables by Match 4824. Function Structures 4925. More About the PEARL Top Level Loop and History Mechanism 5126. Looping and Copying Functions 5427. MiscellaneousVariations and Abbreviations 5528. LowLevel Access Functions 5729. Appendix ofUCI lisp functions added to Franz PEARL 5830. Appendix ofFranz lisp functions added to UCI lisp PEARL 6231. Bibliography 3332. Index of Global Variables and Functions With Their Arguments 6433. Concept Index 71

Update of ChangesThrough

PEARL3.8April 1983

Table of Contents

1. Introduction 762. Running PEARL 76

2.1. Under Franz lisp 765. Accessing Slots of Structures 7610. Printing Structures, Symbols and Other PEARLObjects 76

10.1. Abbreviations 7711. Error Messages, Bugs, and Error Handling Abilities 7812. Short-Circuiting and Redirecting Create Using !, $ and Atoms 7813. More Flexible Hash Selection 7816. Attaching Hooks to Structures (If-Added Demons) 7917. Creating and Manipulating Multiple Data Bases 8019. Creating Expanded Subtypes of Previously Defined Objects 8020. Fetching Expanded Structures 8021.2 The Matching Process 8026. Looping and Copying Functions 8129. Appendix of UCI Lisp functions added to Franz PEARL 8132. Index of Global Variables and Functions With Their Arguments 8134. Compiling lisp+PEARL Files 82

Using the PEARL AI Package

(Package for Efficient Access toRepresentations in Lisp)*

Michael DeeringJoseph Faletti

Robert Wilensky

Computer Science DivisionDepartment of EECS

University of California, BerkeleyBerkeley, California 94720

February 1982

1. Introduction

PEARL (Package for Efficient Access to Representations in lisp)is a set of functions for creating hierarchically-defined slot-fillerrepresentations and for efficiently and flexibly inserting and fetchingthem from a forest of data bases. Its intended use is in AI programming and it has been used at Berkeley in the development of severalAI programs including PAM [7] and PANDORA [8].

PEARL has the expressive power found in other AI knowledgerepresentation languages, but is extremely time-space efficient. Forexample, using a data base of 4000 entries, PEARL takes only about4.2 CPU milliseconds for an average unsuccessful query and 7.3 CPUmilliseconds of an average successful query on a PDP-10.

This document describes PEARL's use and is intended for thebeginning user. (A description of the implementation of PEARL will beavailable shortly.) The best way to approach PEARL is to read thisdocument up through section 11 and then to take it to a terminal andreread it, typing in the examples and observing their effects.

PEARL was implemented by Michael Deering and Joseph Faletti.It was originally developed on a DEC PDP-10 under UCI lisp and wassubsequently moved to a DEC VAX 11/7B0 under Franz Lisp with helpfrom Douglas Lanam and Margaret Butler. Both PEARL and its documentation are still being developed, improved, and debugged. Anycomments or suggestions will be appreciated. Send them to JoeFaletti via Arpanet or Unix mail (Kim.FalettidBerkeley orucbvax!kim.faletti).

• This research was sponsored in part by the Office of Naval Research under contractN00014-BO-C-0732 and the NationalScience Foundationunder grant UCS79-0«54S.

PEARL Documentation Page 2

2. Running PEARL

PEARL is implemented as a set of functions compiled and loadedinto lisp. Thus the full power of lisp is available in addition to theadded power of PEARL.

Since PEARL runs under two different lisps on two differentmachines, there are a few differences between versions. Most of thesedifferences are in the method of starting PEARL up and in the namesof external files accessed by PEARL. The two parts of this sectiondescribe how to start up PEARL either under Franz lisp or under UCIlisp. You need only read the section which is applicable to your lisp.

2.1. Under Franz lisp

To access PEARL, simply run the core version of lisp containingPEARL. On Berkeley Unix, this is available by typing the command:

% ~bair/bin/pearl

or, if ~bair/bin is in your search path, simply:

% pearl

During the startup process, PEARL will read in two files, .init.pearland .start.pearl, if they exist. These files are designed for purposessimilar to those of .lisprc. However, they split these functions intotwo groups. In your .init.pearl file you should include any expressionswhich change the user-settable parameters to PEARL. (For example,methods for setting the size of data bases, the print function, and theprompt are described below.)

When you wish to have other files read in at startup time, thisusually needs to be done after PEARL's parameters are set. PEARL isset up so that after the reading of .init.pearl, it sets any necessaryparameters which you have not set in your .init.pearl and then readsin the file .start.pearl if you have one. This is where any processingwhich requires the attention of PEARL (such as the existence of itsdata bases) should be placed. Thus .init.pearl is primarily for initializing PEARL and .start.pearl is for starting up your use of PEARL.Note: unlike most Unix programs which look for startup files only inyour home directory, thereby limitting you to only one environmentfor each program, PEARL looks for each file first in the current directory and if there is none, then it looks in your home directory. Thisallows you to tailor invocations of PEARL to the kind of work you do ina particular directory.

PEARL Documentation Page 3

After reading in these two files, PEARL will then place you in amodified prompt-read-eval-print loop, with a default prompt ofMPEARL> M. This can be changed by setting the special variable•peariprompt* to the desired value. If you want the standard lispprompt "-> " to be used by PEARL, you must set •peariprom.pt• to nilin your .init.pearl and PEARLwill do the right thing.

The primary feature of the PEARL prompt-read-eval-print loop isthat it uses a different print function. The default function is

(lambda (*printval*)(valprint •printval* 4))

but this can be changed to whatever you desire by giving a new function definition to pearlprintfn. The PEARL prompt-read-eval-printloop also contains a number offeatures to improve upon the standardLisp top level. These include a history mechanism and are describedin chapter 25.

There are quite a few functions from UCI lisp which have beenadded to PEARL to make it easier to move programs to Franz lisp. Alist of these with brief documentation of differences is included in anappendix.

2.2. Under UCI lisp

To access PEARL, simply run the core version of lisp containingPEARL. On the Berkeley KL-10 system, this is available by typing thesystem call

RU PEARL[5500.504,PEARL]

During the startup process, PEARL will read in two files, 1N1T.PRL andSTART.PRL, if they exist. The file IN1T.PRL is designed for purposessimilar to those of INIT.LSP. In this file you should include anyexpressions which change the user-settable parameters to PEARL.(For example, methods for setting the size of data bases, the printfunction, and the prompt are described below.) If you wish to use theREALLOC function to enlarge your memory space, this call should bethe last call in your INIT.PRL file.

When you wish to have other files read in at startup time, thisusually needs to be done after the REALLOC. The common kludge withUCI lisp to solve this is to define an IN1TFN (initialization function)which does this and then to reset the IN1TFN to nil which returns youto the standard Lisp prompt-read-eval-print loop. However, PEARLsets the 1NITFN for its own purposes so that this common "solution"will not work. Instead, PEARL is set up so that after the reading ofINIT.PRL, it sets any necessary parameters which you have not set inyour INIT.PRL and then reads in the file START.PRL if you have one.This is where any processing which requires the attention of PEARLshould be placed. Thus INIT.PRL is primarily for initializing PEARLand START.PRLis for starting up your use of PEARL.

After reading in these two files, PEARL will then place you in amodified prompt-read-eval-print loop, with a default prompt of"PEARL> ". The M>" portion is the (modified) lisp prompt which isprinted whenever read is invoked and can be changed with the UCIlisp function INITPROMPT. The "PEARL" is PEARL's addition and canbe set by setting the special variable •pearlprompt* to the desired

PEARL Documentation Pa*e *

value. If you do not want any prompt added byPEARL other than thelisp prompt you must set •pearlprompt* to nil in your INIT.PRL andPEARL will do the right thing.

The main feature of the PEARL prompt-read-eval-print loop isthat it uses a different print function. The default function is

(lambda (*printval*)(valprint *printval* 4))

but this can be changed to whatever you desire by giving the functionpearlprintfn a new definition. Note that dskin and the break packagehave been changed slightly to also use of this print function. Also,although the functions names and examples below are in lower case,PEARL in UCI lisp expects them all in upper case, just as the rest ofthe UCI Lisp functions.

3. Creating Simple Objects.PEARL allows four basic types of objects. The first two are

integers and arbitrary lisp objects and are created in the usual lispfashion. The second two are structured types provided by PEARL, andare stored in an internal form as blocks of memory. These lattertypes are called symbols and structures.

3.1. Defining SymbolsSymbols are PEARL's internal atomic symbols. SemanticaUy

they are like lisp atoms, but are represented and used differently tomake PEARL more efficient. Before they are used, symbols must bedeclared (and thus defined to PEARL) by a call to the function symbol.which takes as arguments any number of atoms whose names will beused to create symbols. For example,

(symbol John)

creates one symbol called John and

(symbol Bob Carol Ted Alice HomeOffice School Healthy NewYork)

creates several symbols at one time. Symbol is an nlambda (fexpr)and returns a list containing the names of the symbols it created. Aone-argument lambda (expr) version is available as symbole.

There are two ways to get at the actual (unique) symbol: you canuse the function getsymboi or you can evaluate the atom whose nameis built out of the symbol name with the characters s: on the front.The function symatom will build this atom for you when given a symbol name. For example, to set Bto the symbolBob use any of:

(setq B (getsymboi 'Bob))(setq B s:Bob)(setq B (eval (symatom *Bob))

Given an internal symbol, you canfind out its print name by passing itto the function pname (which also will return the print name ofothertypes of PEARL objects).

PEARL Documentation Pa8e 5

3.2. Defining StructuresStructures in PEARL provide the ability to define and manipulate

logical groupings of heterogeneous data and are essentially objectswith slots and slot fillers. As such, they act more like "records" mPascal or "structures" in C than Lisp lists. In reality they are morethan both, but for the moment the reader should keep records inmind.

Just as you must define the form of a record in Pascal beforedefining the value of a variable whose type is that kind of record, it isnecessary to define each particular form of structure you wish to usein PEARL before creating an object with that form. PEARL providesone function called create which is used both to define kinds of structures and to create individual instances of these structures. (Onefunction is provided for both because a special individual is createdas a side effect of each definition. More on this is provided in section7 on defaults.) The first argument to create distinguishes between acall which defines and one which creates an individual. There arethree kinds of defining calls (base, expanded and function) and twokinds of instance-creating calls (individual, pattern) to create. Onlyone of each (base and individual) is described in this section. Therest are left for later.

To start off with an example, let us suppose that you wish torepresent the conceptual act "PTrans" from the Conceptual Dependency (CD) notation of Schank. (The examples in this documentationassume a passing familiarity with CD but lack of this should not hurtyou too badly and PEARL itself does not restrict you in any way to CD.PTrans stands for Physical Transfer which has four "cases": actordoing the transfer, object being transferred, original location andfinal location.) First we must define the form which PTrans structureswill take. In C this would be a type definition for the type PTrans asfollows (assuming a system-provided definition of the type symbol):

struct PTrans {symbol Actor,*symbol Object;symbol From;symbol To;

!;In Pascal this would be

type PTrans = recordActor : symbol;Object : symbol;From : symbol;To : symbol

end;

In PEARL,

(create base PTrans(Actor symbol)(Object symbol)(From symbol)(To symbol))

PEARL Documentation Page 6

does the job. Note first of all that in order to define a new form ofstructure, the first argument to create must be base. Note also thatthe second argument to create is the name of the structure form tobe created. Following this is a list of (<slotname> <type» pairs.Structures are currently allowed to have up to 32 slots in FranzPEARL or IB in UCI lisp PEARL as long as all slots within a particularstructure have mutually distinct names. Different structures mayhave slots of the same name. Thus in applications of PEARL to CDtwenty different structure types might all have an Actor slot.

Five types are allowed for slots: symbol, struct int lisp, and•etof <type>. Symbol and struct are the types just described. Int isa normal Lisp integer value. The type lisp allows arbitrary non-atomic Lisp values. Finally, setof <type> allows you to define setsconsisting of all symbols (setof symbol) or all structures (setofstruct) and can be done recursively (setof setof struct).

4. Creating Individual Instances of Defined StructuresOnce you have defined a specific form of structure like PTrans,

you can create an individual PTrans using individual as the first argument to create and the name of the base structure you want an individual instance of as the second argument. The rest of the argumentsare (<slotname> <value>) pairs in which the <value> must be of thetype that the slot was declared to be. The slots may be listed in anyorder and need not be in the same order as denned. For example, tocreate an instance of John going home from the office (i.e.. JohnPTransing himself from the office to home) you would use this call tocreate:

(create individual PTrans(Actor John)(Object John)(From Office)(To Home))

Create will return an object of type PTrans. with the slots filled in asindicated. The object returned has been created and stored as ahunk of memory in Franz Lisp or a block of memory in Binary Program Space in the UCI lisp (rather than Free Storage where mostLisp objects are stored). Since you are using the PEARL prompt-read-eval-print loop, the object returned by create will be printed inan external list form, something like the above. However, if you printa structure using the standard lisp print functions (as for examplesome break packages will do), it will be printed by Franz lisp m thenormal way it prints hunks. (Warning: Since the structure actuallycontains a circular reference to another hunk, this will cause problems with programs which do not set prinlevel in Franz Lisp so general packages which you wish to add to PEARL should be modified touse some PEARL print function.) With UCI lisp's normal prmt function, it will show up as an address in Binary Program Space, lookingsomething like "#31534".

As with any lisp function that returns an object, we must store (apointer to) the result of create somewhere (for example, in the atomTrip) if we wish to reference it in the future. Otherwise, the createdobject will be inaccessible. (This point is clearer if you consider thatPascal would insist that you do something with the result of the

PEARL Documentation **&e 7

function call, although PEARL and many languages like lisp and Cinwhich every subprogram is a value-returning function allow you toconstruct a value and then blithelygo on your way without usingit)

To store (a pointer to) the instance returned by create in theatom Trip, you could do the following:

(setq Trip (create individual PTrans(Actor John)(Object John)(From Office)(To Home)))

Since this is a common operation, create provides the option of having (a pointer to) the newly created instance automatically assignedto a lisp atom. This is accomplished by including the name of theatom as the third argument to creaie. If the third argument to createis an atom rather than a (<siotname> <value>) pair, create stores thenew object in this atom Thus the effect of the previous example canbe achieved by:

(create individual PTrans Trip(Actor John)(Object John)(From Office)(To Home))

(In addition, when create base PTrans is used, an assignment isautomatically made to the atom PTrans. thus making the defaultin-stance of a structure easily available. To preserve this, calls tocreate of the form (create individual PTrans PTrans ...) are disallowed (that is, ignored). In case you should actually wish to use theatom PTrans for other purposes, evaluating the atom built byprepending i: onto the structure name will give you the defaultinstance of a base structure and evaluating the atom built byprepending d: will give you the actual definition. Changing the valueof these atoms is very dangerous. Given the name of a structure, thefunctions instatom and defatom will construct these atoms for you.For more information about the item assigned to PTrans andi:PTrans, see the section 7 on defaults.)

PTrans is an example of a structure whose slots are all of thetype symbol. Amore complex example is that of MTrans (MentalTransfer: an actor transfering a concept (Mental Object) from oneplace to another (usually from himself to someone else). The MObjectslot is some other act and so would be of type struct resulting in thefollowing definition:

(create base MTrans(Actor symbol)(MObject struct)(From symbol)(To symbol))

A sample instance of MTrans is John told Carol that he was goinghome from the office and would be created with

PEARL Documentation Pa8e 8

(create individual MTrans InformLeaving(Actor John)(MObject (PTrans Leaving

(Actor John)!Object John)From Office)

* (To Home)))(From John)(To Carol))

Note that to fill a slot of type struct with a structure value within acreate one just embeds the appropriate arguments for a recursivecall to create, except that you may leave out individual since it wouldjust be repetitive. If you should want to create an object of anothertype within an individual or base structure, you must include thealternative argument (individual, base, pattern, expanded, or function) before the type name. This is particularlyusefulwhen you wishto create a pattern with an individual instance in one of its slots.

The optional third argument of an atom name for storing in maybe included at each level if you wish. In the example above, createactually will create two new instances, an MTrans which will be storedin InformLeaving. and a PTrans which is pointed to by the MObject slotof the MTrans and is also pointed to by Leaving. In this case, neitherInformLeaving nor Leaving is required. If Leaving were left out, thenone would still have a way to get at the PTrans via the MObject slot ofthe MTrans that InformLeaving points to. However, if InformLeavingwere left out and the result of the call to create were not stored anyother way. there is one more way that the MTrans would be accessible. The value of the most recently created object is always stored inthe special variable «lastcreated* by create so the value ofthe MTranswould remain accessible until the next call to create. Note that ifthere are recursive calls to create during this time in order to process structure values in slots, the value of Hastcreated* is continuallychanging to the most recent one and the setting of •Jostcreated* isthe last thing create does. There is also a special variable called•currenttopcreated* which is set by create at the top level call assoon as the space for an individual or default instance is allocated.Since it is sometimes handy for a piece of user code which runs during create (see the sections on !, $. predicates and demons) to be ableto access the topmost object, •currenttopcreated* is sometimes quiteuseful.

As in C and Pascal, one can embed to any level Create does nothave facilities for more complex networks of structures, as there areother functions in PEARL which allow their construction. Q-eate ismainly used to create objectsfor other functions to manipulate.

5. Accessing Sots of StructuresIn C and Pascal one can access the slots of a record or structure

by using dot notation. For example, in Pascal the To slot of the MObject slot of the MTrans pointed to by InformLeaving would be accessedwith the expression lnformLeaving.MObject.To (or perhaps more accurately InformLeaving-.MObject-.To since slots really contain pointersto objects). In Pascal and C, there are essentially only two things thatone can do to a slot of a record or structure: access it (get its value)

PEARL Documentation Page 9

and assign to it (give it a new value). In PEARL the macro path provides a large number of ways to access and/or change the values inslots of individual structures. (In the UCI lisp version this is calledppath to distinguish it from the system function path.) Acall to pathis of the following general form:

(path <Selector> <Structure> <Slot-Name-or-list> <Value>)<Selector> determines the action to be performed and is notevaluated. <Structure> should evaluate to the object in which theslot occurs (or in whose depths the object occurs). <Slot-Name-or-list> should evaluate either to the atom name of the slot desired in<Structure> or a list of the slot names which one must follow to getdown to the slot. <Value> (which is only needed when it makes sense)should evaluate to the value to be put into the slot (or otherwise usedin performing the function). At this point, we will only describe thetwo <Selector>s corresponding to accessing and assigning. These aregetand putrespectively. Thus to access the value of aslot, you woulduse

(path get <Structure> <Slot-Name-or-Iist>)(No value is needed; the purpose of this call is to get the value.) Toassign a value to a slot, you would use

(path put <Structure> <Slot-Name-or-list> <Value>)For example, to access the Actor slot of the PTrans in Trip, either ofthe following is appropriate:

(path get Trip 'Actor)(path get Trip '(Actor))

This is essentiallyequivalent to a reference to 7"rip~.j4ctor in Pascal.To access a slot within a structure in a slot of type struct, add

the slot names to the <Slot-Name-or-List>, just as we access embedded fields within fields in Pascal by adding more slots to the accessingexpression. For example, to access the place John told Carol he wasgoing inour MTrans example above, we want theTo slot of theMObjectslot of the Mlrans stored in InformLeaving:

(path get InformLeaving '(MObject To) )This is essentially equivalent to a reference toInformLeaving*.MObject*. To in Pascal. PEARL will check each slotreference, andwill indicate if a slot name is not found (perhaps due toa misspellingor an unbound slot).

Similarly, to change the Actor ofour PTrans inTrip to be Bob:(path put Trip '(Actor) (getsymboi 'Bob))

and to change the To slot within the MObject of the MTrans:(path put InformLeaving '(MObject To) (getsymboi 'School) )

which is essentially equivalent to assigning a value toJnformLeaving~.MObject~.7b in Pascal. Note that the order of thearguments to these functions is in not like the argument ordering ofprutprop.

CAUTION: Path does* not check values to ensure that they are ofthe correct type before putting them in a slot Also, a change in a

PEARL Documentation Pa8e 10

structure with path does not cause it to be reinserted in the database (see the next section). Thus, before changing a structure, oneshould remove it from the data base and then reinsert it after thechange.

These functions were gathered under the macro path because oftheir similarity. However, if you prefer to have the action being performed lead off the function name in keeping with putprop, get, put*,getd, etc., these two functions are also available a* putpath and get-path with similar names also provided for all the other forms of pathdescribed below. Thus the name "path" may be tacked onto the endof one of the action selectors to path but the rest of the arguments tothese functions remain the same.

There are quite a few other operations which are allowed throughpath which you will not need or understand until you have read therest of this documentation. We describe them here for completenessbut suggest you skip to the next section during your first reading. Ifyou feel there is one missing, feel free to suggest it since they areeasy to add.

path clear or clearpath - sets the selected path to the standarddefault value for its type (nilsym, nilstruct, zero or nil). Notethat this is only the standard default and does not inherit adefault from above. See section 7 for more on default values.path addset or addsetpath - add the specified value to aslot oftype setof.path delset or delsetpath - delete the specified value from aslotof type setof.path getpred or getpredpath - get the list of predicates on theslot.

path addpred or addpredpath - add the specified predicate tothe predicates on the slot.path delpred or delpredpath - delete the specified predicatefrom the predicates on the slot.path gethook or gethookpath - get the assoc-list of hooks(demons) on the slot.path apply or applypath - arguments to this function are a<Function-or-Lambda-Body>. followed by the <Structure>. and<Slot-Name-or-Ust>. The <Function-or-Lambda-Body> is appliedto the value of the slot. (In the UCI Lisp version. apply# uuMdso that macros will work. In the Franz lisp version, a PE^L-supplied version of apply called apply* is used which also handlesmacros "right".)(Skip this next paragraph until you have read about hashing and

the data bases.) The method of processing the path in path functionsallows a form of indirection through the data base that is sometimeshelpful when you use symbols in slots as unique pomters to otherstructures. Suppose you had the following definitions:

(create base Person!* Identity symbol)

Name lisp))

PEARL Documentation Page 11

(dbcreate individual Person!Identity John)Name (John Zappa))

and you want to ask "what is the Name of the Person in the Actor slotof Trip (above/' which you might normally write as:

(getpath (fetch (create pattern Person(Identity ! (getpath Trip 'Actor) ) ))

'Name)

This is very hard to understand. Ashorthand for this is the following:(getpath Trip '(Actor Person Name))

which behaves like this: when path gets to the symbol in the Actorslot of Trip, it notices that there is still more path to follow. It theninterprets the next symbol inthe path as the name of a type and doesa quick equivalent of fetch of a Person with itsfirst slot set toJohn. Itthen continues to follow the path specified in this new structure,finishing up with the value of the Name slot of the structure. (Notethat this depends on Person structures being hashed by the relevantslot and will fail otherwise. Also note that the tendency of most usersof PEARL has been away from the use of symbols as indirections tolarger structures and toward actually putting the larger structure inthe slot. In this case this would imply putting the Person structure inthe Actor slot of PTrans and eliminate the need for "Person" in thepath list.)

6. Storing InandRetrieving From the DataBase-The Simplest WaySo far we have shown how to create structures and have treated

them pretty much like C structures or Pascal records. However,PEARL's most important departures from these languages involve itsdata base facilities. PEARL's data base can be thought of as one largebag into which any structure can be placed. The data base can holdhundreds, even thousands of separate objects. There are two basicoperations that can be performed upon the data base, inserting withthe function insertdb and retrieving with a combination of the functions fetch and nextitem.

6.1. Storing In the Data Base: Insertdb and RemovedbWhile the simplest forms of these actions are relatively straight

forward, the power and efficiency ofPEARL derives from the nuancesand controls available with these functions which take up much of therest of this document. Much of the power develops from knowledgeprovided by the user about how each kind of structure is likely to beretrieved (and therefore how it should be stored). Thus, the database benefits from knowing as much as possible in advance about theobjects that will be placed within it. This information is provided byusing a large variety of extra flags during definition calls to create. Itis used by insertdb to hash objects into a specific hash bucket in thedata base, by fetch to retrieve the correct hash bucket from the database, andby nextitem to filter the desired objects from thisbucket.

PEARL allows the construction and use of multiple data baseswhich are described in detail later. Without exerting any effort, adata base is automatically created called «maindb* and pointed to by

PEARL Documentation Pa«e 12

the special variable •db*. In general, all PEARL functions which dealwith a data base have an optional last argument which specifies whichdata base to use. If it is missing, then the default data base pointedto by *db* is assumed. Thus you can change the default data basesimply by assigning the desired data base to *db*. For simplicity, thisoptional data base argument is notmentioned in the following discussion.

The function insertdb takes a single structure argument andinserts it into the data base. In its simplest form insertdb requires nouser flags onthe definitions of structures. In this case, insertdb simply bashes on the type of the structure regardless of its specific contents so that each entry ends up in a bucket with all other entries ofthat type. For example, to insert into the data base the PTrans whichwas saved in the lisp variable Trip above, you simply provide it as anargument to insertdb:

(insertdb Trip)We could also put the PTrans (saved in Leaving whose To slot waschanged to School) which was the MObject of the MTrans above in thedata base with:

(insertdb Leaving)Since no information has been provided by the user about how toefficiently distinguish PTranses in general, these two will be stored inthe same bucket (as will all PTranses). When inserting an item into abucket, insertdb will check to ensure that this specific item is notalready in that bucket (using eg) and will only insert it if it is notalready there, thus avoiding duplicates.

The function removedb takes a single structure argument andremoves it from any place in the data base where it has been putusing eg to determine equality.

Since one often wants to create an individual and then insert itinto the data base, there is a macro dbcreate provided whose arguments are precisely like create. Thus, (dbcreate individual PTrans....)expands into (insertdb (create individual PTrans ....)).

6.2. Retrieving HashBuckets from the Data Base: FetchThe simplest case of fetching from the data base is equivalent to

asking if a particular, completely defined object is in the data base.This is performed by a combination of the functions flfetch and nextitem. The first step is to retrieve the hash bucket(s) for the object.For example, to determine whether the object stored in Trip is in thedata base, the first step is to call the function retch and store what itreturns (the form ofwhat is returned is described below):

(setq PotentialTrips (fetch Trip))

The function fetch takes a single structure argument which iscalled the pattern. What fetch returns includes this pattern and thehash bucket(s) from the data base which contain those structureswhich are most likely to "match". The rules of "matching" are fairlycomplex and are described in detail in section 20, but for the momentit is enough to know that two structures match if their respectiveslots contain equal values. Thus matching is closer to lisp's equal

PEARL Documentation Page 13

than to eg.

6.3. Accessing the Results of a Fetch: Nextitem.Conceptually, what fetch returns is a restricted type of stream.

A stream is a "virtual" list, whose items are computed only as needed.When a fetch from the data base is performed, the pattern provided isonly used to construct a stream containing that pattern and the appropriate hash bucket from the data base; no matching (comparing)between the pattern and objects in the data base occurs. Thus thestream contains pointers to all data base items in the same hashbucket, regardless of their likelihood of matching the pattern. Therefore, the stream or "virtual list" returned by fetch is in fact biggerthan the list of actual items which match. (For this reason, the default PEARL print function only prints how many potential items arein the stream.)

For our purposes, you should regard the object that fetch returns to be a funny sort of black box, whose only,use is as an argument to the function nextitem. Nextitem will compute the next element to be removed from the stream. When elements are extractedfrom the stream with the function nextitem, the pattern is "matched"against successive items from the hash bucket until one matches(and is returned) or until the potential items run out (and nil is returned).

Nextitem is very much like the function pop in lisp because itupdates the status of the streamto reflect the extraction of the "topmost element" similar to the way pop replaces its list argument withits caV. Nextitem does this by destructively modifying the stream(but not the hash bucket); once the top item has come off it is nolonger a part of the stream, like pop, nextitem returns nil if thestream is empty.

A stream, as returned by fetch in PotentialTrips, willnever be nilbut instead will be a list. In all cases, the first element will be theatom •Stream*. In most cases, the second element (cadr) is the pattern (object being fetched) and the rest (cddr) is the hash bucketthat the object hashes to. However, it is entirely possible for the hashbucket to either fail to contain any instances of the object, or to contain multiple instances of the object. The form that is printed byPEARL's default print function is: the atom •stream:*, the object being fetched, and the number of potential items in the stream, avoiding the prining of a lengthy result. (If the pattern is actually a function structure, then the atom used is •function-stream:*.)

Thus, to actually determine whether the object in Trip is in thedata base, it is necessary to ask for the nextitem in the bucket of thestream PotentialTrips (that is, in the cddr) which matches the objectbeing fetched (that is, the cadr of PotentialTrips):

(setq FirstResult (nextitem PotentialTrips) )(setq SecondResult (nextitem PotentialTrips) )

If nothing matching Trip occurred in the data base, FirstResult wouldcontain nil; otherwise, it would contain an object in the data basewhich matches Trip. If you have typed in all the examples we haveshown you above, then FirstResult will contain the same value asTrip.SecondResult will be nil. (The only other object in the same bucket is

PEARL Documentation Pa8e 14

the value of Leaving, but that does not match because its To slot contains School after the path put above.) If the two structures m Tripand Leaving both contained the same slot fillers, they would bothmatch Trip and each would be returned by nextitem on successivecalls.

This is essentially the only type of fetching that is useful with theinformation presented so far. but more powerful types will bedescribed shortly.

Since the functions create, fetch, and nextitem are often used incombination, several macros combining them are provided by PEARL:

When you wish to create a pattern only long enough to use it asan argument to fetch, you can use the macro fetchcreate whichis defined in such a way that (fetchcreate blah) is equivalent to(fetch (create blah) ) ).If you want to do a fetchcreate in a function definition and youwish the pattern to be created only once but used each time thisfunction is called (a potential savings in space and time), themacro inlinefetchcreate will call create when it expands andthen expand to a call to fetch with this pattern.If you want to do a create in a function definition and you wishthe object to be created only once, the macro inlinecreate willcall create when it expands and effectively replace itself with theresult.

When you wish to fetch but only need the resulting stream longenough to use it as an argument to nextitem, you can use themacro firstfetch which is defined in such a way that (firstfetchblah) is equivalent to (nextitem (fetch blah) )).If your only goal in fetching some fully-specified object is to testfor its existence in the data base, the function indb which expects a single structure argument will return nil if it is notthere,and non-nii if it is. (Note that indb uses streguot rather thanmatch.)It is sometimes convenient to have a list of all the items whichwould be returned by successive calls to nextitem on a stream.The function streamtolist expects a stream argument and returns a list of the items whichthe stream would produce.

7. The DefaultValues for Unspecified SlotsWhen creating an instance of a given type, one may not always

wish to fill in all the slots of the structure, either because the slotvalue is unknown or immaterial. PEARL has a mechanism for fillingunfilled slots with default values. The simplest form of defaulting involves two predefined objects, nilsym and nilstruct Ndsym is asymbol and roughly corresponds to lisp's nil when nil is viewed as anatom. Nilstruct is a structurewithout anyslots, and corresponds to anull structure. In the absence of a specified value. PEARL will fill inslots of an individual instance of a structure being created with wtf-symif the slot type is symbol, nilstruct if the slot type is struct, zeroif the slot is of type int, and Usp nil if the slot is of type lisp or setof<any type>. Note that it is upto the user to decide upon the meaningofnilsym and nilstruct during further processing. For example, youmust decide for your own application whether a nilstruct filling the

PEARL Documentation Pa8e l

MObject slot of a MTrans indicates that nothing was said or that whatwas said is unknown.

Often you may desire closer control over the default values of aparticular slot within individual instances. For example, suppose youhad a definition ofPerson that includes several pieces of informationabout a person:

(create base Person(Identity symbol)(Age int)(Salary int)(Health symbol))

The Identity slot would be filled inwith a unique symbol for that person (such as the symbol John), the Age slot would contain the age inyears, the Salary slot would get the person's monthy salary mdollars,and the Health slot would contain a symbol indicating their state ofhealth. Now in creating an individual instance ofa Person the Identityslot should be always filled in, but we may desire the other slots to bedefaulted to 30 (years), 20000 (dollars) and Healthy. However, underthe default system described so far, these would be defaulted to zero,zero and nilsym. PEARL provides the ability to specify individual defaults for eachslotofa particular structure type. This is done at basecreation time by following the type within a slot with the new defaultvalue. Thus our definition of Person would be:

(create base Person(Identity symbol)(Age int 30)(Salary int 20000)(Health symbol Healthy) )

Although the main purpose of a call to create base is to define at structure, it also creates a special individual of the type being definedwhich has its slots filled with the default values. For this reason thisindividual is called the default instance of that type. It is a structureinstance like any other, only a pointer to it is kept with the typedefinition, and it is consulted whenever an instance of that type isconstructed. Thus the default values (either the user-defined defaultsor the standard defaults) will always be used whenever the user declines to fill in a slot of a structure instance. For more on defaults,see the discussion of inheriting in section 19 on creating expandedstructures.

& Using Patterns For More Flexible and Powerful RetrievalIf the fetching mechanisms described so far were the only sort of

fetching thatwe could do. fetch (and PEARL) would not be very useful.What is needed is a way to only partially specify the values m thestructure to be fetched. Note that the default mechanism does notaccompUsh this, since all slots are specified at creation time, even ifthey get nilsym, nilstruct, or nil for a value. What is needed is theability to specify a dbnt care value for slots whose values should notaffect the matching process during retrieval. The easiest way to accomplish this in PEARL is to create a type of object called a pattern,Apattern is similar to an individual instance of a structure exceptthat a special pattern-matching variable called ?*any* which means

PEARL Documentation Pa8e 16

dbnl care or match anything is used as the default value forunspecified slots. (The reason for its name will be clear after thedescription of pattern-matching variables later in this section. Evenmore detail on pattern-matching variables and more powerful patterns is provided in sections 21-23 on the matching process, blocks,lexically scoped variables, and the unbinding ofvariables.)

Patterns are created with'create using pattern as the first argument. Other thanthis, their syntax is exactly the same as individuals.An example of a. pattern creation is:

(create pattern PTrans JohnWentSomewhere(Actor John)(Object John))

This pattern would match any instance of PTrans in which John wasboth the actor and the objectbeing moved. (Note that this pattern isstored in the lisp variable JohnWentSomewhere in the same way asother individuals.) The main use of patterns is as arguments to fetch,as in:

(setq PotentialGoings (fetch JohnWentSomewhere))Fetch will return a stream containing all PTranses in the data base inwhich John was the actor and object, regardless what the From and loslots contain. More complex examples can be created. Patterns canbe embedded as in:

(create pattern MTrans InformJohnGoingSomewhere(MObject (PTrans (Actor John)

(Object John))) )

Since all unspecified slots are filled with ?-any*. this pattern will return any MTranses concerning any of John's PTranses when passed tofetch. Thus, ifwe insert InformLeaving from above in the data base:

(insertdb InformLeaving)then the following will fetch that object:

(nextitem (fetch InformJohnGoingSomewhere) )Usually one is interested in amore specific piece ofinformation.

For example, if you knew that John told Carol something and wantedto find out what it was, then you could do this two ways. One is tocreate apattern, fetch it and look atthe MObject slot of theresult:

(create pattern MTrans WhatDidJohnTellCarol(Actor John)(From John)(To Carol))

(setq Result (firstfetch WhatDidJohnTellCarol))(path get Result 'MObject)

However, you might prefer to use a pattern which explicitly showsthat you want that value and gives you a slightly easier way to get atit In this case, you can specify a pattern-matching variable m theMObject slot of the pattern. A pattern-matching variable is createdby preceding an atom with a question mark ? as in?What. The question mark is a read macro character which reads the next atom andbuilds the list pvar* What) out of it (or ('global* What) if What has

PEARL Documentation PaSe 17

previously been declared global to PEARL; see below for more on global variables.). During matching, this variable will get bound to thevalue of the slot it gets matched against:

(create individual MTrans WhatDidJohnTellCarol(Actor John)(MObject ?What)(From John)(To Carol))

(firstfetch WhatDidJohnTellCarol)To access the value of a pattern-matching variable in a structure, oneuses either the function valueof (which is an expr) or the fexpr var-value. Both functions have two arguments: the name of the pattern-matching variable whose value you want and the structure it occurs in(which is evaluated internally by varvalue). Thus both of the followingare equivalent:

(valueof "What WhatDidJohnTellCarol)(varvalue What WhatDidJohnTellCarol)

0. w»rtriwe Structures During Creation For More Efficient RetrievalBesides specifying what type each structure is, the simplest

piece of information that insertdb would like the user to give itthrough create concerns which slot(s) of a type would be most likelyto contain unique information about a particular instance of thattype. This information is used to differentiate instances of the typefrom each other, so that they will be hashed into different hash buckets. This is similar to the "keys" that many data base systems askfor For PTrans, the Actor slot is often the best choice for this role.For Person, the Identity slot would be the best choice for this role.Such unique slots are indicated to create when defining a type byplacing an asterisk '•' before the slotname in a (<slotname> <type»pair. For example, our new definitions of PTrans and Person to takeadvantage of this would look like:

(create base PTrans(•Actor symbol)( Object symbol)( JYom symbol)( To symbol))

(create base Person(•Identity symbol)( Age int 30)( Salary int 20000)( Health symbol Healthy))

If you execute this when you have already executed the other examples in this document. PEARL will warn you that you are redefining thebase structures PTrans and Person. This is all right, since that is precisely what we want to happen. However, the previous instances ofPTrans will remain hashed in the more inefficient way and will notmatch any later PTrans structures that are defined. If you find thesewarnings annoying when redefining structures, they may be turned offby setting the special variable •warn* to nil instead of the initial t.(Note that the lisp scanner requires a space (or other white space) to

PEARL Documentation Pa8e 1B

separate the asterisk from the slotname. Otherwise one would havethe slotname *A:tor).

Any number of starred slots may be provided within a structuredefinition, but usually only one or two are necessary. How one decides which slots should be starred is an art, and dependssignificantly on your application and the nature of your data. Thebasic rule of thumb is to choose those slots whose values vary themost widely from instance to instance. Abad choice will not usuallycause the system to bomb or operate incorrectly in any way. butwhen it comes time to fetch an object back out of the data base thesystemmay have to take the time to scan a large amount of the database rather than finding the desired objectvery rapidly. Thus execution time is usually the only penalty one pays for an improper choiceof slots to star.

However, there is one type of use of a slot which can cause problems in combination with hashing information. It involves the use ofpattern-matching variables and will serve as a useful example of howto use variables and of how insertdb and fetch use the hashing information to insert and find objects. The problem situation occurs whenyou wish to insert items into the data base which contain avariable inthe starred slot (representing general knowledge) andthen use. as apattern, a structure with that slot filled. Thus, the following sequencewill fail to find Thing in the data base and instead will returnnil:

(create base Thing(• One int))

(dbcreate individual Thing DBThing(One ?0)(Two 2))

(nextitem (fetchcreate individual Thing PatThing(One 1)(Two 2))

This fails simply because of the hashing. Let us see why. When insertdb is asked to put something into the data base, it seeks to put itas many places as the hashing information indicates are good placesto want to look for it. With no hashing information at all. the onlything insertdb can do is to put the object with all other objects of itstype. Thus, with no hashing information, all Things are put togetherin the same hash bucket. With the hashing information, insertdbwould like to put DBThing ina second place based on the fact that it isa Thing and on the value of its One slot. Unfortunately, its One slothas an unbound variable in it and does not provide any informationwhich is useful. Thus the hashing process puts DBThing mto the database in only one place. However, when/etc/i indexes PatThing. it usesthe hashing information to determine that it should lookin the database under the combination of Thing and 1. Since DBThing is notthere, it is not found. If we remove the asterisk, this sequence will return DBThing with ?0 bound to 1because both DBThing and PatThingwill getindexed into the same spot (along with all other Things). Thusyou should be very careful when determining how to hash types ofstructures when you intend to insert them into the data base withvariables in them.

PEARL Documentation Pa*e 19

After more of the system has been described and examples offetching and inserting have been given the user will have a betterunderstanding of this process.

As another example, let us now create and insert an instance ofour new PTrans which has the Actor slot starred:

(dbcreate individual PTrans Trip(Actor John)(Object John)(From Office)(To Home) )

This would insert Trip with all other PTranses and. because of thestarred slotActor, also with any otherPTranses with avalue of John inthe Actor slot. Next we redefine and recreate the MTrans:

(create base MTrans(• Actor symbol)( MObject struct)( From symbol)( To symbol))

(create individual MTrans InformLeaving(Actor John)(MObject (PTrans Leaving

(From John)(To Carol))

(Actor John)(Object John)(From Office)(To Home)))

reinsert the PTrans from the MTrans:

(insertdb Leaving)and finally create and insert two other instances ofaPTrans. one withdifferent values in the From and To slots and one with different valuesin the Actor and Object slot:

(create individual PTrans Trip(Actor John)SObject John)From Home)

(To School) )

(create individual PTrans(Actor Ted)(Object Ted)!From Office)To Home) )

Note that this last PTrans will be indexed under the combination ofPTrans andTed andthus will not be in the same hash bucket we lookinwhen fetching Trip (which is indexed by PTrans and John):

PEARL Documentation Pa8e 20

(setq PotentialTrips (fetch Trip))(setq Result (nextitem PotentialTrips)PotentialTrips

Notice the form of the stream PotentialTrips at this point.

10. Printing Structures. Symbols and Other PEARL ObjectsAs mentioned in the beginning, PEARL stores symbols and struc

tures (and their definitions) in hunks of memory that are circularlylinked, lisp cannot print out the contents of these blocks in a usefulway. Franz Lisp sometimes goes into an infinite loop trying to printthem and the best UCI lisp can do is tell you its address, like #2934,which is not very informative. As mentioned above, the PEARLprompt-read-eval-print loop knows how to print these in symbolicform. However, when you want your own programs to print them,PEARL provides you with two pairs of functions to convert theseblocks into more readable form. The first we will discuss is the function valform. Valform takes a struct, a symbol orany other type ofPEARL or lisp object as an argument and returns a lisp S-expressiondescribing the object. Thus if one calls valform on ourPTrans in Trip:

(setq TripAslist (valform Trip))the Lisp variable TripAslist will contain the S-expression:

(PTrans (Actor John) (Object John) (From Home) (To School))Note that valform does not cause the PTrans to be printed out at theuser's terminal, it is merely a function that returns an S-expression(just as the lisp function list does.) PEARL functions will operate uponstructures and symbols only when they are in their internal form, sothe primary reason for converting structures to S-expressions is toprint them (or to modify them for use as new input to create). SoPEARL provides a more useful function valprint that is effectively(sprint (valform <argument>) ). (Sprint is a function provided byUCI lisp or Franz PEARL which prints a lisp expression in a nicely indented form. There are more details on sprint in the appendix on UCIlisp functions added to PEARL.) Valprint is normally used within alisp program to print out any PEARL construct onto the user's terminal and it is also used by the default print function in the PEARLprompt-read-eval-print loop. Try typing the following and notice thatthey are the same except that thesecond value is slightly indented.

(valprint Trip)Trip

like sprint, valprint will accept a second integer argument telling itwhich column to start printing in. The default pearlprintfn uses avalue of 4 for this argument to make the items typed by the usermore distinguisabie from the results typed byPEARL.

There is one other form of each of these two functions. The functions fullform and fuUprint are like valform and valprint but theyprint more complete information. If you type

(fuUprint Trip)you wiU notice that the result has two mysterious nits in each slot.These represent other forms ofinformation (predicates and hooks or

PEARL Documentation Page 21

demons) which can be added to structures and which will bedescribed later. At the moment therefore, valform and valprint areall that the user need remember.

Note also from above that when a pattern with ?*any* is printed,only the name of that variable is printed, and not its value. (Try typing JohnWentSomewhere and InformJohnGoingSomewhere if you donot remember what this looked like.) If a PEARL pattern-matchingvariable has not been bound. PEARL indicates this by printing novalue. If a variable is bound, both the variable name and its value areprinted. Later when you learn how to put your own variables in slots,this will become more useful.

When given a data base, these functions assume that the userdoes not really want the complete contents of the data base printedout and so simply print (database: <databasename>). To actuallyhave the complete contents of a data base printed out, use the function printdb. With no argument, it prints the default data base. Otherwise, it expects its argument to evaluate to a data base. A printfunction which prints all the internal information contained in astructure or its definition is debugprint.

11. Error Messages, Bugs, and Error Handling AbOitiesDue to the complex implemention of PEARLand the lack of facili

ties in Lisp for easily distinguishing between types of input, a user'serror in using PEARL will not show up as soon as it occurs, but may instead cause some unfathomable part of PEARL to bomb out sometimelater. If this should happen, the user might try using the lisp tracefacilities, but wiU often find little useful information. This sad state ofaffairs is currently unavoidable due to the difficulty of catching usererrors where they first occur. This is partly due to our inability topredict what kinds of errors users are most likely to make.

PEARL checks as much as it can, but many features are impossible or prohibitively expensive to check. The best strategy for theuser to foUow is to examine his last interaction with PEARL If the error occurred in the bowels of create, then there is a good chance thatthe user did something wrong in the details of a slot description inthe call to create, since gross structural errors in such calls arechecked for. Inspect your call closely.

Other errors can be even more difficult, since a function call mayblow up or fail to produce the desired result due to bad data passedto create several calls ago. A general rule of thumb to use in trackingdown mystifying errors is to check out the definitions of the structures involved in the function that failed. Thus if path blows up, oneshould determine the type of the structure passed to path, and thencheck the create call that denned that type.

Sometimes PEARL may appear to the user to be doing the wrongthing, but due to PEARL's complex semantics, the user is really atfault. To make matters worse, there is of course always the chancethat the error reaUy is in PEARL. Every effort has been made tominimize this chance, and at the moment there are no known majorerrors (except those indicated in this documentation), but as withany complex evolving software system there is always the chance ofobscure errors. It has been our experience that most errors are dueto the user (including the implementors) not completely understand-

PEARL Documentation Page 22

ing the semantics of some PEARL feature. This documentation is aneffort to minimize this type of error. For any error whichyou commitin which PEARL gives what you consider an unreasonable response,feel free to report it and we will consider trying to catch it. These orany other complaints, bugs or suggestions should be mailed to JoeFaletti via Arpanet or Unix maU (Kim.Faletti@Berkeley orucbvax!kim.faletti).

12. Short-Circuiting and Redirecting Create Using !, S and AtomsSometimes, when creating an individual structure, one may want

to fill a slot with an already created structure that is pointed to bysome atom or returned by some function (or with whatever type ofvalue the slot requires). In this case, one does not wish to (or cannot)describe the value for a slot as a list of atoms. To handle this situation, PEARL allows you to Ust a Lisp expression which evaluates to thedesired internal form (that is, a form which needs no processing bycreate), preceding it with an exclamation point "!". The structure (orother object) resulting from evaluating the lisp expression will betested to ensure it is the. right type of value and, if it is, inserted inthe newly created structure as the value of that slot. (The mnemonicidea of this symbol is as a sort of "barrier" meaning Stop processinghere.'!! and take this (almost) literally!!!) For example, after using

(create individual PTrans Ptrans23!Actor John)Object John)

(To Home))

to create an individual PTrans, leaving it in internal form in the atomPtrans23, you can then insert this PTrans into a new MTrans with:

(create individual MTrans(Actor Bob)(MObject » Ptrans23)(To Carol) )

At other times the user may want to take the result of evaluatingsome Lisp code and splice it into the lisp expression describing thestructure being created at the point where the description of thevalue of a slot would occur. In this case, you wish some Lisp code tobe evaluated and then you wish create to build a value for this slot byfurther processing (scanning) the result of this evaluation. To thisend, PEARL will evaluate any slot value preceded by a "S' and insertits result into the create call, proceeding to process it just as if theuser had typed it in directly. So if one stores the atom Alice in Namewith

(setq Name 'AUce); the atom Alice, not the symbol Alice; (or the value of s:Alice).

or possibly

(setq Name (read) )

with the user having typed Alice, then $ Name in

PEARL Documentation Page 23

(create individual PTrans(Actor $ Name)(Object $ Name)(From Home)(To NewYork) )

is equivalent to having Alice .typed as the Actor and Object values:create evaluates Name and then processes its value Mice as input.Thus, the PTrans will be equivalent to

(create individual PTrans(Actor Alice)(Object Alice)(From Home)(To NewYork))

The power of this construct occurs when Name is a atom whose valuechanges at run time (as when it is read above) or the create call iswithin a loop in which Name takes on many different values.

In summary, both ! and $ cause the evaluation of the lisp expression following them. However, ! stops the usual processing and expects an internal value, whereas $ continues the usual processing andexpects a lisp description of the value. When you need either ! or $.you will know it! Until then, do not worry if you do not understandthem very well!

There is currently one type of slot which allows the effect of !without requiring that the ! be typed. In int slots, if PEARL finds anatom which evaluates to an integer, rather than an actual integer, itwill evaluate the atom and store the result as the value of the slot.(PEARL will eventually be modified to try this in other cases also.)

13. More flexible Hash Selection

The use of stars (asterisks •) to indicate useful slots for hashingdescribed earlier is only one of many hashing schemes that PEARL allows. This section describes the others, and how the user can controlthem. The first point to note is that even with the star hashing a single structure can be hashed in several different ways. Thus if onethinks that in a particular program PTrans will be frequently fetchedfrom the data base given only the Actor or only the Object (that is.the program might only know the Actor and desire the whole PTrans,or know only the Object and desire the whole PTrans) the user shouldstar both the Actor and Object slots within the definition of PTrans.When PEARL stores a PTrans into the data base, it will index it underboth (PTrans + Actor) and (PTrans + Object) in addition to the usualindexing with all other PTranses. In general, any number of slots canbe starred.

Another type of hashing does not use the type of the structure increating a hash index. If the symbol colon (:) is used before the nameof a slot, objects of that type will be hashed under that slot value only.Thus if the Actor slot of the PTrans definition was preceded by a coloninstead of a star, then instances of PTrans would be hashed under thevalue of the Actor slot alone rather the value of the (PTrans + Actor).Ibis would be useful in the case in which one were interested in fetching any structure in which a particular value, say the symbol John,appered in a coloned slot. For example all structures in which John

PEARL Documentation Page 24

appeared in the Actor slot could be fetched at once (and veryefficiently).

A third type of hashing is star-star or double-star (**) hAahiwg ifa double star is used instead of a single star, PEARL will use triplehashing. Only one triple hashing is allowed per structure. Triplehashing requires that two, and only two slots be double starred. IfPTrans were to be defined in the following way:

(create base PTrans(•• Actor symbol)(•• Object symbol)( From symbol)( To symbol))

then when an instance of a PTrans is created, it will be hashed intothe data base under a combination of the three values (PTrans + Actor 4- Object). As with all hashing, if a slot is necessary to a particulartype of hashing but is unfilled (or filled with nilsym or nilstruct) thehashing will not occur. Triple hashing is used when one wants fast access to all individuals of a particular type with two slots likely to havefairly unique values. In the case of PTrans. this would allow one fastaccess to all PTranses in which John PTranses Mary somewhere. Distinctions this fine are not usually necessary, and as it is slightly moreexpensive at creation and fetching time, it should only be used whenthe user is sure of the need for it.

A fourth type of hashing is colon-colon or double colon (::) hashing. It has the same relation to colon hashing as double star hashinghas to star hashing. If the **'s in the above are replaced with ::, thehashing will be on (Actor + Object) ignoring the fact that the structure is a PTrans. This might be useful in fetching all structures involving John and Mary. As with double star hashing, double colonhashing should be used sparingly and only one such hashing pair maybe used in a type.

However, it is possible to combine the use of any of these hashingmethods in a single structure. Thus one could have both double colonhashing and double star hashing, as well as several • and : bashings aswell. Given several ways, PEARL uses the one which the most complexone is used during fetching, since that should provide the greatest degree of discrimination between items (that is, most likely to narrowdown the choices). If the value in a slot intended to take part in hashing is unbound or otherwise not useful, then the next most specificmethod it used. Given the values which are considered useful and thehashing information for the type of structure, the hierarchy of buckets to be chosen is as follows:

•* hashing:: hashing• hashing: hashing

In addition to these four methods of hashing, there are several hashing labels which are modifiers on these methods and affect whatvalues are used to compute the index.

The remaining hashing flags do not introduce any new types ofhashing, but rather modify the way the existing types work. To

PEARL Documentation Page 25

motivate these, consider the implementation of Goal withing CD. Inearly versions of CD, there were several different types of goals, including Delta-Prox (goal of being near something), Delta-Poss (goal ofpossessing something), and so on. In general these delta goals wereof the form (Delta-<some CD primitive> (Actor ...) (Objective ...) ).This lead to an explosion of Delta-goals (e.g. Delta-Move-Fingers-Within-Telephone-Dial), and a new way of handling goals was established. This was simply that all Goals were to have the form:

(create base Goal(Planner symbol)(Objective struct))

where the Objective would be filled with the appropriate structure,whether it was a simple Poss or the $DialPhone script. This changemakes CD much cleaner, but poses somewhat of a problem for hashing. One of the major uses of hashing within some AI programs written in PEARL is to associate plans with goals. So it is best if this process is efficient.

As an example of this problem (using the early form of Delta-goals):

; Declaration of PlanFor rules.(create base PlanFor

!• Objective struct)• Plan struct))

(create base Delta-Prox(Planner symbol)(Location symbol))

(create base Walk-Plan(Planner symbol)!From symbol)To symbol))

; Store in the data base the fact that walking is a way of accomplishing; a Delta-Prox goal.(dbcreate individual PlanFor

(Goal (Delta-Prox (Planner ?X)(Location ?Y) ))

(Plan (Walk-Plan (Planner ?X)(From nilsym)(To ?Y) ) ) )

This structure simply says the fact that if one has a goal of beingsomewhere, then one plan for doing this is to walk. Or, using the rulein reverse, if you note that someone is walking to some location, thenyou might infer that they had a goal of being at that location. Notethat after being put into the data base, the rule can be easily fetchedby presenting either half of it as a pattern.

Thus if a planning program has a goal of doing the action in theatom GoalAct. then it can query the data base for any direct plans fordoing Act by:

PEARL Documentation Page 26

(fetchcreate individual PlanFor(Goal ! GoalAct)(Plan ?»any*))

So if GoalAct happened to be a Delta-Prox goal, then the rule abovewould be fetched. However the revised form of goals hides the uniquenature of the Delta-goal, and the best one could do is fetch all PlanForrules that have a structure of type Goal in their Goal slot. This is aserious loss smce all PlanFors have a Goal in their Goal slot; thus thesystem would have to look through all PlanFors whenever it was tryingto fetch one. What is needed is a way of telling PEARL that when hashing on Goals, never hash the structure type Goal, but rather use theitem that fills the Objective slot of the Goal. This would solve ourproblem nicely, as now all PlanFors would be hashed on the name ofthe Objective (Prox, Dial-Phone, etc.), and a list of all PlanFors wouldnot have to be searched to find a particular one, rather the systemcould just hash directly to it.

To indicate to PEARL that this hash aliasing is desired, place anampersand '&' before the slot name to be substituted for the structure name when defining the structure. Thus Goal would be declared:

(create base Goal( Planner symbol)(& Objective struct))

Naturally only one slot can be selected for hash aliasing.In this way. Goals change the way in which other structures use

them to index but the way in which Goals themselves are indexed willnot be affected. Since many other types of structures are likely tocontain Goals, we must be careful about how this affects the hashingof all of them. It might be the case that PlanFor was the only structure indexed based on Goals which would benefit from hash aliasingand that some structures would actually be hurt by this because theyexpected Goals to be only one of many types of values. In this case,putting the control of how Goals get used by other structures into thedefinition of Goal is a bad idea. Instead, the control can be moved upinto only the problematic structures. These structures can simplyadd the ">" hash label to a starred slot, causing PEARL to use the firststarred slot of the slot-filling structure instead of its type. For example, when we put a both "•" and ">M on the Goal slot ofPlanFor then itwill always use the first starred slot of the Goal in its Goal slot:

(create base GoalPlanner

Objective struct))! Planner symbol)

(create base PlanFor(• > Goal struct)( Plan struct))

Thus, the use of ">M hashing is called forced aliasing since the structure filling a slot has very little control over it.

However, there is one way for a structure to affect how forcedaliasing happens. If the user wanted to also star the Planner slot ofGoal but wanted the Objective slot to be used in cases of forced aliasing, then the use ofan "-*" on the Objective slot will allow that:

PEARL Documentation Page 27

(create base Goal!• Planner symbol)• ~ Objective struct))

thus allowing Goals inserted directly into the data base to be indexedby the combinations Goal + Plannerand Goal + Objective while otherobjects containing Goals would use the Objective slot rather than GoalOtherObject + Objective.

On the other hand, if most structures containing Goals wouldbenefit from the use of the hash aliasing label "&" in Goal, but onlyone or two are hurt by it, the use of "&" in Goal can be overridden bythe structures which will contain Goals by adding the "<" hash label tothe starred slot to produce anti-aliasing. This gives the controllingstructure the last word over how it is hashed.

(create base Goal( Planner symbol)(k Objective struct))

(create base OffendedStructure(* < Slot struct))

Thus, the anti-aliasing "<" means jfust for this hashing, turn off hashaliasing (if any) of any structure filling this slot.

The proper use of hash aliasing and anti-aliasing, like all thehashing specifiers is an art that must be learned by applying them toreal systems, and the correct hash directives for a particular systemrely critically upon the statistics of that particular system operatingupon a particular set of data. The hashing mechanism was designedto give the user benefit in proportion to the effort expended in determining hash labels. With no effort, the structure type provides somehelp. With the addition of each label or pair of labels, an item to beinserted into the data base is indexed into another location in thehash table. Thus the cost of extra labels is simply the time to findanother hash bucket (a few adds and multiplies), and add the item tothe front of the list implying the time and space incurred by onecons-cell.

14. Using Predicates to Constrain FetchingSometimes when you are creating a pattern to fetch a structure,

giving the overall form of the structure is not specific enough. In particular, it is often desirable to restrict the value of a slot to asubrange. For example, using the structure Health:

(create base Health(Actor symbol)(Level int) )

one might want to find out who is sick by creating a pattern that onlymatches those Health structures in which the Level is less than -1 (ona scale from -10 to 10 perhaps). This can be done by simply writing apredicate (say Sick) which expects to be given the value of the slotbeing matched against as its one argument:

(de Sick (Num)(lessp Num -1))

PEARL Documentation Page 28

Then you simply add its name after the value within the <slotnamefiller> pair of the pattern:

(create pattern Health HealthPattern(Actor ?Person)(Level ?Level Sick))

Given these definitions, a (fetch HealthPattern) would pass the Levelslotfiller of each Health structure it found in the data base to thepredicate Sick. If Sick returned true (non-nit) then it would considerthe slot to have matched whereas a nil from Sick would be considereda mismatch. There are no standard predicates for users to use forthese purposes, but they are relatively easy to create as needed.

However, one often has a predicate which has more than one argument only one (or none) of which are the slot value. For example,one might want to include a special variable or the value of some other slot of the structure or the structure itself. To make this easyPEARL allows predicates to be arbitrary s-expressions which may contain any of several special forms for which PEARL substitutes thecurrent slot or structure.

If a predicate includes an asterisk +, this is replaced by the valueof the current slot (in the structure being matched against). If it includes a double asterisk +•, this is replaced by the whole structurebeing matched against. If you want the value of another slot in thecurrent structure, precede its name with an equal sign (as in =Slot-Name to have the value of the slot named SlotName inserted). Thereis a readmacro "=" which converts =5 into (*slctm S), just as the read-macro "?" converts ?X into (*var* X) (or pglobal* X)) for pattern-matching variables. While processing predicates before executingthem, PEARL will look for these three constructs and replace any ofthem with the appropriate value, so pattern-matching variables canalso be used in predicates.

If there are several predicates on a slot, they are run in succession until one returns nil or they have all been run. Thus, a list ofpredicates provides the effect of a conditional and Thus, althoughPEARL knows nothing special about logical connectives like or andand, the effect of a the usual lisp and is automatically implied andthe conditional or of Lisp can be had by using the s-expression type ofpredicate. If you wish things to run regardless of their results, providing the effect of unconditional and, use hooks (demons).

The above was one of two types of predicates available. Tomotivate the other type, consider the case of wanting to fetch allMTranses about the occurence of a PTrans. This could be accomplished in one of two ways. The first is:

; Jn this pattern example, all slots are automatically filled; with ?*any* except the MObject which must be a PTrans.(create pattern MTrans

(MObject (PTrans)))

Since this method actually results in ?*any* being matched againstthe fillers in each of the PTrans's slots, it is a bit inefficient.

The second way uses structure predicates to avoid this matchingby specifying merely that the filler of the MObject slot must be aPTrans structure. This is done by listing the name of a previously

PEARL Documentation Page 29

defined structure after a pattern-matching variable:(create pattern MTrans

(MObject ?Obj PTrans))PEARL will then bind Obj to any structure that is a PTrans (or expanded PTrans) and match successfully without examining any of the slotsof that PTrans. PEARL can tell the difference between these two typesof predicates since one will have some sort of function declarationand the other will be the name of a defined structure. In the case of afunction with the same name as a structure (which the user shouldnever do as it invites errors) the name's structure role takes precedence.

Since a similar effect is sometimes desired on slots of type symbol, a similar but more complex mechanism is provided with symbolsand with structures which failed the above test. If the name of apredicate on a slot of type symbol or structure is the name of a typeof structure, PEARL will assume that what you want to know about thevalue in this slot is whether there is anything in the database of thetype specified by the structure predicate with the slot value in itsfirst slot. Thus, if the data base contains anitem saying that the symbol John represents a person:

(symbol John)(dbcreate individual Person

(Identity John))

then fetching a pattern with a symbol slot which has a Person predicate on it:

(fetchcreate pattern Thing(Slot 7X Person))

will cause the equivalent of a fetch from the (default) database of thepattern (Person (Identity John)). Note that this implies that the firstslot of a structure enjoys somewhat of a pre-eminence and that thismeans that one should carefully choose which slot to put first. Forefficiency however, fetch is not actually used. The function actuallyused is disguisedas which expects the slot filler, the structuredefinition (not default instance) and an optional data base to look in.Slot filler may be either a symbol or structure.

This second type of predicate can also result in a kind ofinefficiency which you might like to avoid. By putting a variable inthe MObject slot of the MTrans along with a PTrans structure predicate, we preclude PEARL from hashing the object in any useful way,forcing it to look through all MTranses instead of only MTranses withPTranses in their MObject slot. Since patterns are most often lessspecific than the objects in the data base, this can make a bigdifference. Another problem with a variable plus a structure predicate is that the structure predicate is either based on fetches and thefirst slot or it is limitted to matching the type only. We might sometimes want a more complicated structure to be used as a predicate.However, if we opt instead for the more efficient fetching and matching by putting a structure in the slot, we have lost the ability to havea variable bound during the match.

To allow you both to help improve the hashing and matching of astructure and also to bind a variable as a side effect, PEARL provides

PEARL Documentation Page 30

a mechanism to attach an adjunct variable to the slot. This adjunctvariable in a slot is bound as a side effect whenever the values in theslot of the two structures were already bound, have already beenmatched successfully and all predicates and slot hooks have beenrun. Adjunct variables may be local, lexically scoped or global, just asany other variable. To use an adjunct variable, include the variableafter the value preceded by a colon and preceding any predicates orslot hooks. For example.

(create pattern MTrans(MObject (PTrans (Actor John)) : ?0bj))

would match any MTrans about John PTransing something, and alsobind the adjunct variable ?0bj to the actual PTrans structure that applied.

Since PEARL uses hunks to create so many types of values of itsown, it also provides a set of predicates to test an item to see whattype it is. Many of them are quite definitely kludges since theydepend upon certain bizarre structures existing only in PEARL-created items and not in user-created items and thus should not be

depended upon totally. These functions are streamp. databasep,blockp, definitionp, psymbolp (to distinguish from Franz lisp sym-bolp), structurep, symbolnamep, and structurenamep.

15. More Useful Slot Types

These last few examples begin to show the restricted nature ofbasic integer values and of labelling slots as being of type struct. Ifthe values in an integer slot will range between -10 and 10. then youwould like to say that. If the values which will fill a slot of type structure will be Events or Acts or States, you would like to specify that.PEARL provides mechanisms to fill both of these needs.

In the case of an integer slot to be filled with values from a rangeof -10 to 10, these integer values do not represent "levels of health"very well either. Rather than saying that a person's "health level" is-2, you might like to say it was "Sick". In fact, you would probablylike to say that the values of the slot will be one from among the setof values "Dead, Critical, Sick, OK, Healthy and InThePink". Moreover,you might like to specify that these values are to be associated withinteger values in such a way that the ordering you specified holds andyou may or may not want to specify precisely what integer valuesshould be associated with these atoms. In other words, you would likea type which consists of a set of values with a linear ordering on them,similar to the Pascal scalar or enumeration type.

Such a type exists in PEARL and is created by a call to the function ordinal. For example, to create an ordered set of values torepresent levels of various states when you want the actual integervalues to be created by PEARL, you would say:

(ordinal Levels (Low Middle High))

which would associate the numbers 1. 2, and 3 with Low, Middle andHigh respectively. If you want to specify the values to be associatedwith each name, you simply list the value after each name. Thus, tocreate a set of values for use in the integer Level slot of Health above,you might say the following (the values need not be listed in order):

PEARL Documentation Page 31

(ordinal HealthLevels (Dead-10 Critical-6 Sick-2 OK 2Healthy 6 InThePink 10))

Among the actions that ordinal performs are the following:1. The assoc-list of names and values for the ordinal type can be ac

cessed by evaluating the atom built by prepending o: to thename of the ordinal type. Given the name of an ordinal type, thefunction ordatom builds this atom. Thus o:Levels contains (and(eval (ordatom 'Levels)) returns) the value ((Low . I) (Middle . 2)(High. 3)).

2. Atoms consisting of the name of the ordinal type concatenatedwith a colon and the value name are created and set to the valuethey represent. Thus Levels.Low is set to 1, Levels.Middle is setto 2, etc.

3. Two atoms with :min and :max concatenated to the name of theordinal type are created and set to the lowest and highest integer values in the type. Thus HealthLeveJs.-min is -10, andHealthLevels.-max is 10.

4. The name of the ordinal type is added the list of all ordinal typenames kept in the special variable •brdinalnames*.

5. The name of the ordinal type is stored with the slot so that theprint functions can convert from the integer value back into thename. Since the default value for integers is zero but most ordinals will not have a zero value, the print functions will print•zero-ordinal-value* instead of zero.

Having created an ordinal type, it is then possible to declare in astructure definition that a slot will contain values of that type. Theuse of values from this type is not enforced by PEARL but allows thedefinitions of integer slots to be more readable, allows the use of thenames of values instead of their associated integers when creating individuals and allows PEARL to print the more readable informationwhen printing an integer slot. The special atoms created allow predicates, hooks (demons) and other functions to refer to these valueswithout knowing their associated integers. We can now redefineHealth to use HealthLevels:

(create base Health!Actor symbol)Level HealthLevels))

and create an individual which says that John is in the pink of health:

(create individual Health!Actor John)Level InThePink))

Declaring a slot to be of type struct is similarly unenlightening,so PEARL will accept the name of a structure type in its place. Forexample, we can make the following definitions:

PEARL Documentation Page 32

(create base Person(* Identity symbol))

(create base Health(Actor Person)(Level HealthLevels) )

and the Actor slot of Health will be of type struct. However, there iscurrently no extra type checking implied by this declaration(although it is being considered), but again it improves the readability of declarations tremendously.

16. Attaching Hooks to Structures (If-Added Demons)A fairly old construct within AI is that of demons. In their pure

form they could be thought of as asynchronous parallel processesthat watch everything going on within a system, lying in wait for aparticular set of conditions to occur. These conditions might be ablock-manipulating program stacking some blocks too high to bestable, or a data base program violating a consistency constraint.The main problem with classical demons was that in their most flexible form they gobble up far too much system time, as well as beingvery hard to program as it was hard to see just when they might popup during the execution of a program.

In an attempt to control the implementation of demons and atthe same time provide the user with increased control over the built-in PEARL functions. PEARL allows the user to attach pieces of code tostructures that will be run when specific PEARL (or user) functionsaccess particular types of data or pieces of data at particular placesin the code. Thus, PEARL provides a general but restricted and fairlyefficient ability to control the operation of specific functions onspecific pieces of data by providing hooks in the PEARL functionswhich check for requests within structures that certain functions berun when they are accessed in certain ways. Thus PEARL has two useful sub-breeds of hooks which watch over either

a. the value of a particular slot of a particular individual structure,referred to as slot hooks.

b. operations upon all individuals of a particular base structuretype referred to as base hooks.like predicates, hooks can either be the name of a function to

run or a lisp s-expression to be evaluated. If an s-expression, theycan include the special forms ++ representing the current structureor * representing the value of the current slot on slot hooks and ofthe current structure on base hooks. Variables or slot names preceded by = are also allowed Oust as in predicates), referring to variablesor slots in the current structure. If hooks are run by functions whichtake two items as arguments, like match, then the special form >**may be used to represent the other structure (which > is meant tosuggest) and >• may be used for the value in this slot of the otherstructure. (In the case of functions of only one argument. >• and >••are the same as ** and *.) In functions which take two arguments, thespecial form ? may be used to represent the result that the functionintends to return. (This will be *pearlunbound* in hooks which runbefore the function has done its job.)

PEARLDocumentation Page 33

When hooks run in the context of a call to path, two special variables are available: *paLthbap* which is the topmost structure passedto path and •pathlocal* which is the current innermost structurewhose slot is being accessed. When hooks are run in the context of acall to a function which deals with a data base, then the special variable db will contain the data base currently being used.

The functions used to fill in the special forms like *, **, =slot, andvariables before evaluation come in two flavors and are called fillinl

and fillin2. Fillinl is designed for hooks which run on single structures and expects as arguments:

a. the function (s-expression) to fill in,b. the slot value (or item if a base hook) to use for *,c. the structure to use for **, and

d. the definition for the item provided as the third argument (for interpretation of =slot forms).FUlinS is designed for hooks which run on two structures and

produce a result and expects as arguments:

a. the function (s-expression) to fill in,b-c. the slot values (or structures if a base hook) to use for * and >*,d-e. the structures to use for •• and >•*.

f. the definition for the structure provided as the fourth argument,and

g. the result the function intends to return to use for ?.Four functions for running hooks are provided for the user, two

for running slot hooks and base hooks for single items and two forrunning slot hooks and base hooks for pairs of items. Runslothookslexpects to be given the invoking function's name, the structure andname of the slot on which to run the slot hooks, and the value to beused for *. Runslothooks2 expects to be given the invoking function'sname, the two structures and name of the slot in them on which torun the slot hooks, and the values to be used for • and >*. Run-basehooksl expects to be given the invoking function's name and thestructure whose base hooks are to be run. Runbasehooks2 expectsthe invoking function's name, the two structures whose base hooksare to be run and the result the calling function plans to return.

If present, base hooks are run by most major PEARL functions. Ifa base hook is labelled with <foo then the function foo will executethe hook just after entry and whatever initialization is necessary. If abase hook is labelled with >foo then the function foo will execute thehook just before exitting. Slot hooks are run by most major PEARLfunctions which look through the slots of a structure. If a slot hook islabelled with <foo then the function foo will execute the hook just before processing the slot. If a slot hook is labelled with >foo then thefunction foo will execute the hook just after processing the slot.

However, hooks can be turned off selectively or completely. Bysetting the atoms *ninallslothooks* and n-unallbasehooks* to nil youcan completely disable the running of all hooks. This is useful for debugging and also helps improve efficiency a bit if you do not usehooks at all. There is also an atom to go with each PEARL function (ofthe form •run...hooks*) which can be used to disable hooks for select-

PEARL Documentation Page 34

ed functions. The following is a complete table of what PEARL functions run hooks and the names of the labels that invoke them and theatoms that control their running:

Base hooks are run by: invoked by hooks labelled:create expanded <.expanded or >expandedcreate individual . <individual or >individualcreate pattern <pattern or >patternsmerge <smerge or >smergenextitem <nextitem or >nextitemstandardfetch * <fetch or >fetchexpandedfetch * <fetch or >fetchinsertdb <insertdb or >insertdbremovedb <removedb or >removedbnextequal <nextequal or >nextequalindb <indb or >indbstandardmatch <match or >match

basicmatch <match or >matchstrequal <strequal or >strequal

* fetch does not run hooks on function structures.

Slot hooks are run by: invoked by hooks labelled:standardmatch <match or >matchbasicmatch <match or >matchstrequal <strequal or >strequalpath put <put or >putpath clear <clear or >clearpath addset <addset or >addsetpath delset <delset or >delsetpath addpred <addpred or >addpredpath delpred <delpred or >delpredpath get <get or >getpath getpred <getpred or >getpredpath gethook <gethook or >gethookpath apply <apply or >apply

Hooks of both kinds are controlled by these atoms, initially t:*runallslothooks* — controls all slot hooks.*runallbasehooks* — controls all base hooks,•runputhooks* •runclearhooks*•runaddsethooks* •rundelsethooks*•runaddpredhooks* *rundelpredhooks*•rungethooks* *rungetpredhooks*•rungethookhooks* *runapplyhooks**runmatchhooks* *runsmergehooks**Tunindividualhooks* *runexpandedhooks*•runpatternhooks* *runnextitemhooks*•runfetchhooks* *runinsertdbhooks*•runremovedbhooks* •runindbhooks**runnextequalhooks* *runstrequalhooks*

It is likely that hooks attached to a particular function would liketo run the same function in such a way that hooks will not be invoked.Orin general, it is possible that you will want to run some PEARL func-

PEARL Documentation Page 35

tion in such a waythat it is "hidden" from hooks. To make this easy, amacro is provided called bidden which temporarily sets the atom*run...hooks* to nil, runs a command and then restores the formervalue of that atom. J\>r this to work correctly, you must invoke thefunction you wish hidden with the name corresponding to its*run...hooks* atom. Thus, you can hide the creation of an individualfrom hooks by executing:

(hidden (individual PTrans....))

(see Section 27 for the macro individual) butnotbyexecuting:(hidden (create individual PTrans ....))

A parallel function visible temporarily sets the associated atom to tbefore evaluating the function.

One of the reasons that hooks are checked for both before andafter a PEARL function does its job is to provide the user with the opportunity to affect the result of the particular task. In the simplestcase, a hook simply executes a piece of code and does not directlyaffect the function it is labelled with. However, if the value returnedby a hook is a list whose car is either *done*. fall*, and *use*, thenthe action of that function will be modified. If the result of a hookwhich runs before the task starts with *done*, then the hook ispresumed to have accomplished what the PEARL function was supposed to have done and the function will return immediately with thecadr of the hook's result if there is one, or else with the structure being operated on (for base hooks) or the value in the slot (for slothooks). If the result of a hook which runs after the task starts with*done*, then the function will return immediately with the cadr of thehook's result if there is one, or else with the result that was going tobe return anyway.

If the result of a hook which runs before the task starts with•/ait* then the hook is presumed to have determined that the PEARLfunction should quit and the function will return immediately with thecadr of the hook's result if there is one, or else with the atom *fail*.If the result of a hook which runs after the task starts with *fail*,then the function will return immediately with the cadr of the hook'sresult (which may be nil).

If the result of a hook which runs before the task starts with•use* then the hook is presumed to have determined that the PEARLfunction should use a different value instead of the originally providedone and the function will use the cadr of the hook's result for the restof the task. If the result of a hook which runs after the task startswith •use* then the function will replace its intended result with thecadr of the hook's result (which may be nil). Thus, for example, a slothook labelled with <match can short-circuit the matching of a slotand one labelled with <match can reverse the decision made bymatching of a slot. Similarly, a base hook labelled with <match canuse its own matching algorithm and one labelled with >match canmodify the result of the whole match.

Obviously, these all should be used with great care. Note that return immediately means without even running any other slot hookson that slot for slot hooks or without running any other base hooks onthat structure for base hooks.

PEARL Documentation Page 36

For example consider the case of a structure representingsomeone's order in a Chinese restaurant. As items are added to theorder, it would be nice if there was a magical slot TotalBill that contained the current running total of the cost of the items ordered.Demons, being such magical creatures, fill the bill nicely. However,we only wish to have our demon-like hooks activated when particularslots are filled (added to or accessed). First consider the simple casein which an order consists of three items only, the name of the soupand one or two entrees:

(create base Chinese-Food-Entree(Name lisp)(Price int))

(create base Chinese-Dinner-Order(Soup Chinese-Food-Entree)(Entree1 Chinese-Food-Entree)!Entree2 Chinese-Food-Entree)TotalBill int))

(create individual Chinese-Food-Entree(Name (Hot And Sour Soup))(Price 323))

(create individual Chinese-Food-Entree(Name (Sizzling Rice Soup))(Price 349))

(create individual Chinese-Food-Entree(Name (Lingnan Beef))(Price 399))

(create individual Chinese-Food-Entree(Name (Mandarin Chicken))(Price 367))

(create individual Chinese-Food-Entree(Name (Shrimp Cantonese))(Price 479) )

; an undetermined meal is created.(create individual Chinese-Dinner-Order Meal

(Soup ~ if >put (Maintain-Total * ** =TotalBill))(Entreel - if >put (Maintain-Total * ** =TotalBill))(Entree2 ~ if >put (Maintain-Total * ** =TotalBill))(TotalBill 0))

Note that a slot hook is put after the value in a slot by using the wordif (or hook) followed by the appropriate label for the invoking function followed by the function name or s-expression to be evaluated.Note also that when you want to put hooks on slots of an individualbut do not want to specify a value, the use of "~" will instruct createto copy the default value instead. If the Maintain-Total function isproperly specified, whenever one replaces one of the food slots with areal dish using the putpath function, the Maintain-Total function

PEARL Documentation Page 37

would be activated and would add the price of that meal to the running total in the TotalBill slot. If one changed one's mind a lot, itwould be necessary to include another hook Remove-Price whichwould be activated by a clearpath. This would require adding the if'cleared hook "if >clear Remove-Price" after the if-put hook:

(create individual Chinese-Dinner-Order ChangingMeal(Soup ~ if >put (Maintain-Total * ** =TotalBill)

if >clear (Remove-Price • •• =TotalBiU))(Entree 1 ~ if >put (Maintain-Total * •* =TotalBill)

if >clear (Remove-Price * ** =TotalBill))(Entree2 ~ if >put (Maintain-Total * ** =TotalBill)

if >clear (Remove-Price • •* =TotalBill))(TotalBill 0) )

The code for the two hooks follows:

(de Maintain-Total (Food Meal CurrentMealTotal)(putpath Meal '(TotalBill)

(•plus CurrentTotal(getpath Food '(Price)))))

(de Remove-Price (Food Meal CurrentMealTotal)(putpath Meal '(TotalBill)

(•plus CurrentTotal(getpath Food '(Price) ))))

A more flexible meal order structure would not have three slotsfor food, but rather a single slot of type setof struct. Then entrieswould be added by the addsetpath functions, and the if-prut hookwould be an if-addset hook but the code would essentially be thesame.

To attach a base hook to a structure, the first "slot" in itsdefinition must start with one of the atoms if or hook. The rest of theslot must then contain a sequence of labels for invoking functions andfunction names or s-expressions to be evaluated. For example, to invoke valprint before and a user function called verify afterwardswhenever a PTrans is inserted into the data base, you would definePTrans as follows:

(create base PTrans(if <insertdb (valprint * 5)

>insertdb (verify *))(•Actor symbol)( Object symbol)( From symbol)( To symbol))

Recall that PEARL provides a print function called fuUprint whichfor most structures seen so far printed two extra nils in each slot. Ifa slot has predicates, the first nil will be replaced by a list of them. Ifthe slot has hooks, the second nil will be replaced by a list of cons-cells with the invoking function in the car and the hook in the car.

The invocation of hooks labelled with other forms of path aresimilar except for apply. If (path <apply Fen ...) or (path >apply Fen...) is executed, then any hooks which are labelled with Fen will be

PEARL Documentation Page 38

run.

At this point the syntax of a slot in a definition or individual hasbecome quite complicated, so we summarize with the following BNFgrammar: ^*

(a b c } means select one of a, b, or c.XXX ] means optionally XXX.

XXX * means zero or more XXX's* Iy means x or y

<BaseSlot> ::= (<HashLabels><SlotName><SlotType><InheritOrValue><AdjunctVariable><PredicatesAndHooks>)

<IndividualSlot> ::= (<SlotName><InheritOrValue><AdjunctVariable><PredicatesAndHooks>

<ExpandedSlot> ::= <BaseSlot> | <IndividualSlot>

<HashLabels> ::= J "&" "**•" "*'* "**" °jm ••«•• ••>•• ••<•• i •<SlotType> ::= \ "struct" "symbol" "int" "lisp" J |

"setof" <SlotType> | <OrdinalName> |<StructureName>

<InheritOrValue> ::= <Value> | "~" | "nil" |"=="<Value> | ":=" <Value>

<Value> ::= <integer> | <atom> | <list> | <Variable><AdjunctVariable> ::= [ ":M <Variable> 1<Variabie> ::= ?<atom><PredicatesAndHooks> ::= ( <Predicate> | <Hook> | •<Predicate> ::= <StructureName> | <S-Expression><Hook> ::= "if' <atom> <HookFunction><HookFunction> ::= <atom> | <S-Expression>

17. Creating and Manipulating Multiple Data BasesWithout any effort on the user's part, a single data base of a de-

falt size is created by PEARL as it starts up. It is called •maindb* andis pointed to bythe special variable Mb* which is assumed byall functions which use a data base to point to the default data base (that is,the data base to be used when an expected data base argument ismissing).

To build another data base, choose a name for it and call thefunction builddb which is an nlambda (fexpr) expecting the name ofthe new data base. You may build as many as you wish and storewhichever one you want in •db*. If *db* is already bound after your.init.prl file has been read in, then *maindb* will not be built.

Sometimes one may wish to clear out the data base and start out

PEARL Documentation Page 39

with a clean slate. To make this easy, there is a special functioncleardb which expects either zero or one data bases as argumentsand does the job. If it receives no arguments, then the default database is cleared. Cleardb removes everything from the data base, butdoes not actually delete (or reclaim the storage space of) the objectswithin the data base. But if the objects inside are not pointed to byany program variables, they ape gone for good.

Data bases contain two parts, referred to as dbl and db2. Dblcontains items which are indexed under only their type or usingsingle-colon hashing. Its default size is 29. Db2 contains items whichare indexed under two or three values. Its default size is 127. Thesesizes are chosen to be prime numbers which are just barely smallerthan a power of two. (This choice was made to take full advantage ofhunks in Franz lisp are always allocated to be a power of two.) The ratio between the two sizes is approximately 1 to 4. The size for databases may be chosen by specifying the power of two that you wish db2to close to.

The function setdbsize expects an integer between 2 and 13representing the power to which two should be raised. The defaultdata base size is thus the result of calling setdbsize with an argumentof 7. To change the default size, you must call setdbsize in your.init.prl file. The data base size may be set only once and if it is notchanged by the .init.prl file, it is set to the default. (In the Franz Lispversion, although this full range of values is accepted, the largest adata base in the 1 to 4 ratio can be is 29 + 127 since hunks are limit-ted to 128 words. However, an argument of 9 to setdbsize will set thesizes of both data bases to 127.) Related relevant special variables are•tiblsize* and *tib2size* which are set by setdbsize and •availa-blesizes* which contains the assoc-list used to associate the power oftwo to a size.

IB. Creating a Forest of Data Bases

Although having multiple data bases which are unconnected isoften enough, it is sometimes convenient to build onto an already existing data base in a tree-like fashion. For example, in a story understanding program, one might want to have the default data base containing long-term knowledge and then add a data base to contain theknowledge specific to a particular story being processed. In large applications, it can also help to split up special kinds of knowledge toimprove efficiency even more than PEARL'S hashing already does.With only the ability to build separate data bases, searching for a factwhich might be either general knowledge or specific knowledgelearned from the story would require two fetches, one from each database. However, if the story data base is built on top of the main database then simply fetching an item from the story data base will alsoinclude fetching from the main data base. To build another data baseupon an existing one, use the function builddb with two arguments,the name of the new data base and the name of the old one to build

onto:

(builddb *story* *maindb*)(builddb *future* *maindb*)

These two statements will build two data bases on top of the main onesuch that fetching from *story* will look both in it and in *maindb*

PEARL Documentation Page 40

but not in "future*. You can then build further upon any of these ifyou wish. Note however, that the second argument must, be the nameof the data base to build upon and cannot be •do* to build upon thedefault data base. Also, if the second argument is missing, then thenew data base is isolated, not built on top of the default data base.

If your program builds many data bases, it is likely that some ofthem will be temporary ones. If this is so, it is possible to release adata base so that the space can be garbage collected or reused for alater data base. To release a data base, pass the actual data base(not its name) to the function releasedb. If the data base is not a leafof the data base tree, then the space will not actually be released until all its children are released also but PEARL will no longer accept itas a data base argument.

A list of the names of the currently active data bases is maintained by PEARL in the special variable •activedhnames*.

19. Creating Expanded Subtypes of Previously Denned ObjectsWithin CD, as in many applications, you may have many different

structures with some slots with the same name. PEARL allows this, asit can always tell which type of structure you are using, and thus itbehaves just as if you had used unique names for all slots. But sometimes the fact that two different structure types have slots with thesame names is more than a coincidence: there may be various semantic similarities between the similar parts of the two structures.PEARL has a mechanism for creating such structures using the expanded selector to create. Basically, you must first define a basestructure that contains all the identical parts of two or more structures, and then you must define the structures themselves as thebase plus the differences. A good example of this from CD involvesActs. All Acts within CD have an Actor slot, and all of these slots havethe same meaning. That is, whatever is going on, the person in theactor slot is the motivating force. So we may first define this commonpart as a normal base structure:

(create base Act(• Actor symbol))

and then we can define the various acts as expansions upon this base:

(create expanded Act PTrans(Object symbol)(From symbol)(To symbol))

(create expanded Act MTransiMObject struct)(From symbol)(To symbol))

(create expanded Act ATrans{Object symbol)(From symbol)(To symbol))

PEARL Documentation ?*&* 41

(create expanded Act JnjestiObject symbol)Through symbol))

Note that we did not have to list the Actor slot, it was inherited fromthe base structure Act. The structure to be expanded need not be abase structure, but could itself be an expanded structure. Thus wecan capture the similarities of the various Transfers with:

(create expanded Act TransSFrom symbol)To symbol))

followed by

(create expanded Trans PTrans(Object symbol))

(create expanded Trans MTrans(MObject symbol))

(create expanded Trans ATrans(Object symbol))

In expanded definitions as in base definitions one can specify hashingand default information in the usualway. However one can selectivelyinherit some of this information from the structure being expanded.Thus in our first Act example, since we specified star hashing on theActor slot, all the structures that we denned in terms of Act have starhashing on their Actor slot by default. If we had not wanted this forATrans, we could have specified this simply by listing the Actor slotover again without the asterisk. However, since PEARL requires oldslots in expanded structures to also provide a new value, we needsome way to say inherit the same old value. This is done by puttinganup-arrow "-" where PEARL expects to find a value, justaswhen youwant to inherit the default value but add hooks or predicates whencreating individuals.

(create expanded Act ATrans(Actor ~)(From symbol))

We also could have added colon hashing to the Actor slot by Usting itabove as normal. However, we cannot change the type ofa slot andincluding a type name after Actor will cause PEARL to try to interpretthat type name asa value, (resulting in any of several errors, depending on the type). Thus, the hashing information for any slot is inherited from above, unless it the slot appears in the expanded structure.

Default values are inherited in almost the same way. The exception is that if in the original structure the default is preceded by thesymbol ":=" (rather than being preceded by either nothing or thesymbol "=="). expansions of that structure will not inherit this value,but instead will get the standard default for that type. So if onedefines:

(symbol Pandora)

PEARL Documentation Page 42

(create base Act(Actor symbol Pandora))

or

(create base Act(Actor symbol -== Pandora))

(create expanded Act PTrans(From symbol))

then all PTranses will have Pandora as their default Actor, whereaswith:

(create base Act(Actor symbol := Pandora))

(create expanded Act PTrans(From symbol))

only the default instance of Act will have Pandora in its Actor slot andthe default Actor of PTrans will just be the usual default for symbol-valued slots which is nilsym. Which type of default inheritance to usedepends upon the application, and must be decided on a case by casebasis.

Given this hierarchy, it is oftenuseful to checkwhetheran objectis of a certain type or an expanded version of it. Two functions provide this ability with slightly different arguments. Isaexpects an itemand the name of the type you want to check for. Isanexpanded expects two instances. Thus the following are always true for any structure X:

(isa X (pnameX))(isanexpanded X X)

Two related functions are nullstruct and nullsym which are functionsfor testing for nilstruct and nilsym (similar tonull for nil).

20. Fetching Expanded StructuresTo make the extra information that expanded structures provide

more useful, a special version of fetch called expandedfetch isprovided which takes the hierarchy of structures defined into accpunt whenfetching. For example, using the above hierarchical definitions of Act.Trans, PTrans. MTrans. and ATrans, you can insert three differentTranses into the data base:

(dbcreate individual PTrans!Actor Pandora)Object Pandora))

(dbcreate individual MTrans!Actor Pandora)To Pandora) )

PEARL Documentation Page 43

(dbcreate individual ATrans!Actor Pandora)from Pandora))

and then to fetch all Transes performed by Pandora, you could use:

(create pattern Trans TransPattern(Actor Pandora))

(expandedfetch TransPattern)Once you start using expanded structures, you usually want to be ableto use the function name fetch and mean expandedfetch. To this end,the standard fetch function is actually called standardfetch. Thisleaves the function fetch to be bound to whichever fetch function youwish. It is normally given the same function definition as standard-fetch.

21. How Two Objects HatchWhen a fetch from the data base is performed, the pattern pro

vided is only used to construct a stream containing that pattern andthe appropriate hash bucket from the data base; no matching (comparing) between the pattern and objects in the data base occurs.Thus the stream contains pointers to all data base items in the samehash bucket, regardless of their likelihood of matching the pattern.When elements are extracted from the stream with the function nex-titem, the pattern is "matched" against successive items from thehash bucket until one matches (and is returned) or until the potentialitems run out (and nil is returned).

21.1. When Is a Pattern Not a Pattern?To understand the process withwhich two objects are matched,

it is necessary to understand what is meant by a pattern in the context of matching. The term pattern has been used in two ways inPEARL. It has been used previously in this documentation in a specialized sense which is only relevant in the context of creating a pattern. The use of the pattern selector to create is simply a variationon create individual which uses the match-anything variable ?*any*as the default for unspecified slots instead of the usual defaultvalues(either the one inherited from the base definition or the default forthe type of slot). It is called creating &pattern because the change ofdefault is usually only useful for constructing a pattern.

However, the use of the function create with object selector pattern is not the only way to create a patternwhich can be matched; infact, it is only useful for forming simple patterns. Any individualstructure in PEARL can be used as a pattern. If a fully specifiedstructure (that is. one with an actual value in all of its slots) is usedas a pattern for fetching, it will only match objects which are equal toit in a manner similar to equal (versus eg) in Usp. (An exception tothis occurs when patterns with pattern-matching variables are storedin the data base.) Thus a fully specified pattern is only useful fordetermining whether a particular fact (object) is in the data base.Any object is a pattern but the interesting patterns will not be fullyspecified; rather, they will have unspecified slots which containpattern-matching variables instead of values. The details of the

PEARL Documentation Page 44

matching process will now be described.

21.2. The Hatching ProcessIn general, the matching procedure takes two structures and ei

ther, neither or both may contain pattern-matching variables. Soconceptually, both are patterns. If the structures are notdefinitionally the same type then the match fails automatically. Otherwise, each structure is viewed as a sequence of slots which are successively "matched" between the two structures. Two structures ofthe same type match if and only if each of their slots "matches" thecorresponding slot of the other structure. Each slot is ofone of fourtypes (struct, symbol int, or lisp), or is a setof one of these types.Regardless of its type, each slot is filled in one offour ways:(1) The slot may contain an actual value of its type (for example, a

slot oftype struct maycontaina PTrans).(2) The slot may contain a variable which is local to the structure

(pattern-matching variables are local unless otherwise specified).(3) The slot may contain a global variable, declared previously by a

call to the function global with the variable's name as argument.(4) The slot may contain the special match-anything variable ?*any*.If the slot contains a variable (other than ?*any*) which has not beenbound then it maybecome bound as a sideeffect of the matching process. All local pattern-matching variables are unbound at the start ofthe matching process. When a local variable is bound to a real valueduring the matching process (it will never be bound to a variable), itwill not be unbound again but for the purposes of matching will betreated as if the slot were filled with that value.

Let us now examine each of the pairings of slot values which mayoccur and how they are matched. If either of the two slots beingmatched contains the special variable ?*any*. then the slots match bydefinition, regardless of the contents of the other slot. If both slotscontain variables that are unbound, the slots do not normally match,(even if the two variables are textuaUy the same name). (Since someusers want two unbound variables to match, the value to be returnedin this case is stored in the special variable •matchunboundsresult*whose initial value is nil. Setting this variable to non-nil will causetwo unbound variables to match immediately but will not cause ttieirpredicates to be run.) If one slot contains an unbound variable (andthe other a bound variable or a value), then the predicates and restrictions of the slot with the unbound variable are tested, and hooksonthat slot labelled with match are run to see if the unbound variableshould be bound to the bound value. If so. then the unbound variableis bound to the value of the other slot, and the two slots match. Notethat only the predicates and hooks on the structure containing theunbound variable are run while the symbols *. •*. and =<slotname>refer to the other structure (with the bound value in it). If the predicates or restrictions return nil the two slots do not match, the variable is not bound, and the entire match fails.

If both slots contain either bound variables or values, then thevalues of the two slots are compared. If the slot is of type struct,then the entire matching algorithm is recursively applied. If the slotis of types int or lisp, then eguaZ is used. If the type is symbol, than

PEARL Documentation Pafie 45

the two values must be the same symbol. Regardless of the type, restrictions associated with the slot are until one fails or there are nomore to run. All must succeed for the match to succeed. If thematch succeeds, then any hooks with the label match are run.

The difference between the two types of variables is one of scope.Normal variables (for PEARL) do not need to be declared, and may beused in any structure by typing in ?<var> during a create (note thatputpath is incapable of installing variables). The scope of these variables is only over the structure in which they are typed. Thus thevariable ?Vtyped into two different creations of structures are in noway connected (in the same manner as two local variables V indifferent Pascal subroutines are unrelated.) If one becomes bound,the other is unaffected. On the other hand, if a variable name is previously declared as global:

(global G)then all instances of the variable name ?G are the same (similar toglobal variables in Pascal). The list of global variables is kept in thespecial variable •globallist*.

As mentioned before, when two structures are matched, all normal (local) variables in both structures are unbound (bound to thevalue •pearlunbound*) before any slots are compared. This is to ensure that any bindings induced by a previous unsuccessful (or successful for that matter) match are removed. This rule is useful because the type of matching that early PEARL users have needed is inmatching most patterns against, fully-specified values (that is. casesin which one slot is always bound and the other either bound or unbound). Global variables are not unbound before each match, so theycan be used to reflect global contexts. They are given the value•pearlunbound'* at the time they are declared and remain boundthereafter unless explicitly unbound by the user. To unbind a globalvariable, you may use use the function unbind a fexpr which requiresthe name of a (previously declared) global variable:

(unbind G)or use setq and the function punboundwhich simply returns the atom•pearlunbound*:

(setq G (punbound) )The function pboundp will test the value of aLisp (not PEARL) variableto see if it is •pearlunbound9. The function globalp will determinewhether the variable passed to it has been declared global.

Global variables should be used with care so that they are not setby unsuccessful matches. Generally this is achieved by first collecting the value desired into alocal variable via aseries ofmatches (onlythe last ofwhich succeed), and then using the result of this successtocause a further action which is guaranteed to correctly bind the valueof the global variable. (These actions may be hooks which rebind theglobal variable every time the local one is bound Effectively, this is away to say always unbind this particular global variable beforematches. The action also could be performed by the user's programwhen the right value is found.)

Each structure or tree of structures built by a call to create con-

PEARL Documentation Pa8e 46

structs an individual assoc(association)-list of all the localvariables inthat structure. This assoc-list is stored with the root of the tree, thusachieving local uniqueness of variables within a structure. Globalvariables are bound values of the lisp atom of the same name and areaccessed in the usual way. To access the value of a localvariable in astructure, one uses either the function valueof (which is an expr) orthe fexpr varvalue both ofwhich have two arguments: the name of thevariable whose value you want and the structure it occurs in (evaluated internally by varvalue). For example, to get the value of ?G in X,use either of:

(valueof 'G X)(varvalue G X)

Thus PEARLuses both deep and shallow binding.The match algorithm is available to the user as a separate func

tion by the name standardmatch. This function unbinds all local variables before proceeding with the match (using the macro unbindvars)and again afterwards if the match failed. A function which assumesthat all local variables have been unbound already and proceeds justas standardmatch would is basicmatch. The function name used toaccess the matching function by nextitem and all other built-inPEARL functions is match which is normally given the same functiondefinition as standardmatch but can be bound to whichever matchfunction you wish. A function which compares two structures forequality without affecting the values of their variables is available asstrequal. Since it does not bind variables, it also does not executepredicates although it does run base hooks and slot hooks labelledwith strequal. Afunction parallel to nextitem which uses strequal instead of match is available as nextequal.

This rest of this section covers other ways to access and affectthe values of variables. It will make more sense after reading thenext section on blocks but fits in better here so you should probablyleave it for your second reading.

Recall that the question mark read macro expands into either(+var* <vamame>) or (•global* <vamame>). These two forms arenot normally meant to be evaluated. However, for convenience, thereare two functions •?ai* and «]global* which return the value of thevariable whose name is their argument. That is. if IX expands into(•global* X), executing it will returned the value of the atom X. ThusXand ** are equivalent for a global variable. For a local or lexicallyscoped variable, in which ?X expands into (*var* X), the function•var* looks in three places for avariable with the name X.1 First it looks to see if the special variable •eurrentstructure*has

been bound to a structure by the user, and if so, looks in its variable list.

2. If this fails, it looks in the special variable •feurrentpearlstruc-ture* for a structure. This variable is set by various PEARL functions like create, fetch, path, and nextitem to the top level structure they last operated on.

3. If this fails, it looks in the currently open block on top of •block-stack* if there is one.

PEARL Documentation Page 47

4. If this fails, it returns nil.Note that the atom •currentstructure* is there simply for the use ofthe user and is never set by PEARL.

Arelated function is set? which takes a question-mark variable, avalue and an optional environment and sets that variable in that environment or else in the default environment described above to thatvalue. The environment can be either a structure or a block. Thisstops with an error message if it fails to find a variable by that namein the specified or default environment.

22. Binding Blocksof StructuresTogetherViaCommonVariablesIt is sometimes the case that you wish to create a group of struc

tures which are closely related in some way and which you wish to tietogether via pattern-matching variables. For example, &frame mightbe considered such a loosely connected group of structures. In thiscase what is desired is for the pattern-matching variables to actuallybe the same. Normally however, if you create several structures inPEARL with variables having the same name, each has its own localvariable with that name and they are totally unrelated. If on the other hand, you declared them to be global, then all structures havingvariables with that name would refer to the same variable and itwould no be unbound before matching. For this purpose. PEARL provides variables of an intermediate nature which are local to only asmall group of structures and which are all unbound before any one ofthe structures takes parting in matching.

These variables are called lexically scoped (although if the related functions block and endblock are called dynamically, they also provide a breed ofdynamic scoping). To declare a set of lexically-scopedvariables, thus opening a (nested) scope for them, use the functionblock, so named because of the similarity to the concept of a blockinAlgol-like languages. The function block is a fexpr which in its simplest form expects one argument which should be a list of new variables:

(block (A B C))Such a call to block creates an unnamed block containing these variables andany occurrences ofvariables with these names in anystructures created after this call will refer to these lexically-scoped variables. Thus, no structure created after the above call to block cancontain a local variable called A. B, or C. (However, if a variable hasbeen previously declared to be global this overrides all future declarations with block. Once again, global pattern-matching variablesare to be used with extreme caution.)

Ifyou use several blocks, especially nested blocks, it is helpful togive them names. For this purpose, block will accept two arguments,the first an atom to name the block and the second the list of newvariables. For example:

(block Name (A B C))

To end the most recent block, use the fexpr endblock. This function accepts any of three types of arguments. If last block was unnamed, simply use:

PEARL Documentation Page 4B

(endblock)If the last block was named you must provide endblock with thisname:

(endblock Name)This is provided as a protection against unbalanced calls to block andendblock. If you wish to end the most recent block, regardless ofwhat its name is, use

(endblock •)To end several blocks at once, you can use the fexpr endanybiockswhich ends all blocks back through the one whose name matches itsargument. Again no argument (nil) means the last unnamed block.An argument of "•" causes PEARL to end all currently open blocks. Ashorthand for (endanybiocks *) is (endallblocks).

The function block builds an assoc-list of the variables listed. Ifthe block is nested, the assoc-list of the enclosing* block is hooked tothe end of its assoc-list, thus providing a complete assoc-list of all thevariables available in the block. A side effect of block is that thisassoc-list is bound to the name of the block. The block itself (theblock's name plus this assoc-list) is available as b:<blockname> sothat the above call to block binds Name to

((A . •pearlunbound*) (B . •pearlunbound*) (C . •pearlunbound*))and b:Name to

(Name (A . •pearlunbound*) (B . *peariunbound*)(C . *pearlunbound*))

If a block is unnamed. PEARL calls it unnamedblock and thecorresponding variables are set. The special variable •blockstack*contains a stack of all the currently active blocks. The effect of ending a block is to pop it off this stack. Once a block is closed, it is stillaccessible through the Lisp variable b:<blockname>. Given the nameof a block, the function Wockatom will build this atom for you.

It is possible to return to the scope of an earlier block with thefexpr setblock which expects the name of a named block. This willhave the effect of ending all currently open blocks and setting thecurrent block stack to contain this block. Note that this block willcontain all the variables of any blocks it is nested in but that it is notpossible to close off these block selectively. Tnus, the block stackwillcontain only one block with all the variables in its complete assoc-list.

23. Controllingthe Unbinding of Variables by MatchIt is sometimes desireable to use the filled-in result pattern of a

fetch ormatch as a pattern for a further fetch (or match) orto otherwise store and restore the current values ofvariables (for example, toallow backtracking algorithms and/or hypothetical assertions). Sinceall bound local variables would normally be unbound during this further fetching or matching, this would not be possible given the mechanism described so far. To accomplish this action, which can be considered as "pushing" the context ofthe current assoc-list, you shoulduse one of several functions provided for this purpose. The functionfreezebindings takes a structure as argument and moves all bound

PEARL Documentation Pa8e 49

variables from its normal assoc-list to a backup so that fetch will notunbind them. The function thawbindings takes a structure as argument and will undo this action, restoring the assoc-list to its completestate. These two functions affect the structure plus any bound variables in all enclosing blocks. To freeze or thaw only a single structure, use freezestruct and thawstrucL To freeze or thawonly a singleblock, use freezeblock and thawblock which expect the name of ablock as an argument.

Above it was mentioned that two structures will match if and onlyif they both are of the same type. Actually the system has been extended to allow the matching of a structure of one type with anotherof a type derived from the first via a create expanded. The extra slotsofthe larger (expanded) structure are ignored during the match.

Lastly it should be mentioned that the matching rules are anevolving system, and may be amended as experience with theiruse isaccumulated. The rules may seem a bit complex at first, but in usethey are fairly natural. The rules are biased towards efficiency (likemuch of PEARL). The designers felt that hiding exponential time-complexity processing within the language would lead users to construct inefficient programs without realizing it. Thus several"features" of other complex AI matchers are not built in. The usermust implement these individually at a higher level. It has been ourexperience that this leadsto much cleaner designs.

24. Function Structures

In using PEARL, it is sometimes handy to escape into Usp in a,'structured,, way. Although PEARL allows ad hoc escapes by way of itshooks and the ! and $ evaluation operators defined above, the philosophy in PEARL function structures is to allow structured escapes thatrestrict the generality of the escape to the minimum necessary forthe task. At times you may wish to equate Lisp functions with theirexpected arguments with PEARL structures with their associatedslots. For example while you maywish to describe an action in a program as fetching anitemfrom the database, you may actually be unable to describe the item as a structure and/or be unable or unwillingtoactually store it in the data base. Instead, you will sometimes wantthe value to be provided by a function called at fetching time insteadof a structure in the data base.

Take as an example the case ofkeeping track ofwhether anytwoobjects are near each other. One possible way to do this is to keepstructures in the data base which record for each pair of objects thatare near each other the fact that they are near each other:

(create base NearSObjectl struct)Objects struct))

Then determining whether two objects are near each other would require a simple fetch. However, ifyou are dealing with a large numberof objects which are moving around quite a bit but only want to knowabout nearness once in a while, it might be easier or more efficient tocompute whether two objects arenear each other only on demand. Inthis case, you might like towrite a function called Near which expectstwo arguments. However, for consistency, you may notwant to design

PEARL Documentation Pa8e 50

your program so that it knows what things can be fetched and whatthings need computing. So you would like to define astructure whichlooks like our definition of Near above but which actually invokes thefunction Near.

To do this, one may create the function Near (which must be anexpr) and also a structure of typeJunction named Near:

(de Near (x y)... mechanism to actually determine nearness ... )

(create function Near(Object1 struct)(0bject2 struct))

and then can create an individual of it for fetching:

(create individual Near IsNear(Objectl John)(0bject2 Office))

(fetch IsNear)Note that the format of function structures within PEARL is the sameas that of structures. However, the name of the actual Usp functionto be called must match the type name ofthe function structure, andthe arguments must occur in the same order and be of the sametypes as the slots which will contain the actual arguments to the function.

As another simple example, to define a function structure tocorrespond to the function getpath, we would use the following:

(create function getpath(Item struct)(Path Usp) )

and then an actual instance:

(create individual getpath Minst(Item ! Mtransl)(Path '(MObject)))

This example is not too useful. As a more realistic use, consider aprogram to return all the MObjects of ail MTranses that are m thedata base:

(create function nextitem(Stream lisp))

(create pattern MTrans MPatl(MObject ?X))

(global MStream)(setq MStream (fetch MPatl) )

(create individual getpath Minst2(Item (nextitem (Stream ?MStream) ) )(Path '(MObject))

PEARL Documentation Pa*e 51

(setq Streaml (fetch Minst2))Note the recursive use of the data base: the fetch of Minst2 will causea getpath to be executed. But PEARL must first get the two arguments to pass on to getpath which causes the function nextitem to beevaluated, getting the next MTrans in MStream to pass to getpath.

Thus, function structures provide a way to describe a functionand its arguments through a PEARL structure and then to include, ina pattern to fetch or in a structure slot, a function callwhich will provide the desired value at fetching time. However, this only works during fetching.

The function used by PEARL to execute a function structure isevalfcn. It takes an item as its argument and returns the result ofapplying the associated expr to its slot values ifthe itemis a functionstructure. If the item is a single structure it returns the item untouched. If the item is a list of structures, it applies itself recursivelywith mapcar. No other PEARL functions currently know about function structures as being any different than othe*r individual structures.

25. More About the PEARL Top Level Loop and History MechanismThe PEARL prompt-read-eval-print loop includes two features

which make PEARL easier to work with than the usual top level of Usp.Both features were designed in imitation of the Berkeley Unix shellprogram csh.

The first is an aliasing mechanism which provides the ability touse various atoms as aliases for commonly executed s-expressions. Ifyou type an atom to the top level and it has the property alias, thevalue of its altos property will be evaluatedinstead. Thus, if you do a

(putprop 'dir '(dir) 'alias) ;inUCILispor

(putprop 'Is '(exec Is) 'alias) ; in Franz Lispthen if you type the atom dir or Is repectively to the top level, you willget the contents of your directory printed out. Two such built-inatoms are history which will run the function history and print outyour last 64 commands (see below) and h which will print the last 22commands (one crt screenful). The aliasing mechanism can beturned off (saving a get for each atom you use at the top level) bysetting the special variable •usealiases+to nil.

PEARL's top level also includes a simplified command-historymechanism. As you type in expressions to the top level of PEARL,they are stored away for future reference. The results of evaluatingeach expression are also kept. The commands and their results arekept in two hunks whose default size is 64. The hunk containing thecommands is kept in the special variable •history* and the hunk containing the results is kept in the special variable •histval* To changethe number of commands remembered, set the special variable niis-torysize* to something other than 64 in your .init.prl. It cannot bechanged later. (If you are a novice user of PEARL, we recommendthat you not change it to be smaller, since the history command cansometimes be helpful to someone helping you to debug somethingafter you havefiddled withit a while.)

PEARL Documentation Pa8e 52

The commands you type are squirrelled away so that you can askPEARL to re-execute them, thus saving the pain of retyping a complicated expression. To access the previous commands, the readmacro"!" is provided. To access the results of the previous commands, thereadmacro "9' is provided. (The exclamation point is in imitation ofthe cshell; the dollar sign is meant to suggest "value".) These read-macros peek at the next character to determine what to do. We discuss the variations available on these two readmacros in parallel,since many of them coincide.

The simplest and most useful forms are "!!" and "W whicheffectively re-execute and reprint the last command or its result. Actually, both forms are executed, but the dollard signmacro always returns its value quoted so that its effect is usually to just reprint theresult of the previous command. Note that since these are readmacros which simply return the last s-expression typed or its value, youcan use them to build up more complex commands. For example:

pearl> (fetch Item)(•stream:* . . .)

pearl> (nextitem !!)will cause the fetch to be repeated and then do a nextitem on it.However, it is much more efficient to use the SS form in this case,since what you really want is to do a nextitem on the result of thefetch in the last command:

pearl> (fetch Item)(•stream:* .. .)

pearl> (nextitem $$)

The commands are numbered as you type them, starting withzero. Although the values wrap around in the hunks, the historynumber continues to climb. The current history number is availablein the special variable •historynumber*. To access a particular command or its value, you may type you may follow an exclamation pointor dollar sign with the number of the command. Thus !23 and J23 arethe 23rd command and its result. If you don't remember thecommand's number you can use the function name or a prefix of it.Thus Ifetch and Sfetch will access the last fetch or its value. Or !feand Sfe will access the last command starting with fe or its value. Ifthere was a reference to an atom (instead of a list) with that name orwith that as a prefix somewhere, then the atom will be evaluatedagain. For exclamation point, this is awaste of typing except for longatom names. For dollar sign, it provides you a way of recovering thevalue of a variable that has since changed: (As a side effect of implementing this, PEARL contains a function prefix which expects two listsand determines whether the first is a prefix of the second, consideredas a list of atoms. Thus. PEARL just calls prefix on the results of ex-plodemg two atoms.)

Here the parallel between the two macros ends.There are five forms which work only with exclamation point and

refer only to the last s-expression typed. They are essentially ways topickindividual top-level elements out ofthe last command:

PEARL Documentation Page 53

r* the first argument!S the last argument!• the complete set of arguments1:0 the function name!:n the nth argument

Both macros are splicing macros so that their values may be splicedinto the current s-expression. Vis designed so that the following willwork:

pearl> (add 12 3 4)10

pearl> (times !•)(times 12 3 4)

24

To see the last 64 commands you gave printed out, use the function history (or type the atom history). If you don't want all 64 commands, history will accept an integer argument telling how many youwant. Thus the aliases on history and h are:

(putprop 'history '(history) 'alias)(putprop 'h '(history 22) 'alias)

If you use the command numbers often, you might like to have thehistory number printed out before each command. To have the history number printed just before the PEARL prompt, set the special variable •printhistorynumber* to a non-niZ value. The default value isflnilfR.

Whenever you use the ! or $ history mechanisms, the line youtype in will be reprinted in its expanded form on the next line usingthe current pearlprintfn. If youwish to modify your own read macrosso that they also will cause this reprinting, simply have them set thespecialvariable •readlinechanged* to a non-nilvalue.

It is sometimes useful to have a function return no value. Thatis. you often do not want the value of the function to be printed by thetop level loop. In particular, functions which printvalues often returnugly values afterward. To get around this problem, the PEARL top level disables printing of the value returned by a function if it returnsthe atom ♦invisible^ All of the PEARL print functions return thisvalue.

It is sometimes useful to be able to save the current state of aPEARL run for later. There are two functions to allowthis. If you wishto save a version which will continue exactly where you left off (at thetop level), use the function savecontinuewhich expects zero, one ortwo arguments. If you wish to save a version which will read in the.start.prl file when it starts up. use savefresh. (If you also want.init.prl read in, change the value of the special variable •nrststart-up* to t beforehand but be careful not to put functions which mayonly be run once in it.) Note however that you cannot save FranzPEARL on top of the file you are running; trying to will result in theDumplisp failed error message from Franz Usp. Note also that asaved PEARL uses about 1500 blocks or 750kbytes on the disk so thisshouldbe used sparingly. (Exceeding the disk quotawill result in thesame error message.) In the Franz Usp version, if the number of arguments to either of these functions is:

PEARL Documentation Pa6e w

0: It will be saved as pearl in the current directory.I: The argument is assumed to be a (relative) file name to save

under.

2: The result of concatenating the two arguments together with a /between them will be the file name used. (This is for UCI Uspcompatibility.)

In the UCI Usp version, if the number of arguments is:0: It will be saved as pearl in the current directory.1: The argument is assumed to be a file name for the current direc

tory.

2: They must be a directory and a file name to save in.

26. Looping and Copying FunctionsPEARL includes several loop macros. The first two were included

simply for use by the implementation but might be useful to the user.They are the for and while macros which both expand into a progwrapped around aprogn A call to the while macro should be of theform:

(while <test>EXPR1EXPR2

EXPRn)

The <test> is evaluated before each execution of the loop. If it isnon-ni!, the EXPRi are evaluated in sequence. This continues until<test> return nil in which case the last value returned by EXPRn isreturned. Since the while expands into a prog, any of the EXPRi maycall the function return, terminating the loop prematurely and returning the value given to return.

A call to the for macro should be of the form:

(for <var> <initial> <final>EXPRIEXPR2

• ••

EXPRn)

<initial> and <final> should evaluate to integers. The EXPRi are repeatedly evaluated in sequence with <var> being set to the values ascending from <initial> to <final>. If <initial> is greater than <final>,nothing is done. <var> is a prog variable which disappears after thefor executes. The value returned is the last value of EXPRn and return provides a premature exit with avalue as in while.

The fexpr foreach expects a stream and a function (or macro)and applies the function to each element returned by successive callsto nextitem on the stream Unfortunately it only returns nil at thistime. Eventually, other usefullooping structures may be provided.

Since PEARL provides several new types of values, it provides afew functions to copy them In particular, the standard Usp functioncopy has been redefined to avoid trying to copy anything that isnotacons-celL There are several ways to copy structures, describedbelow. The rest of PEARL values either are too complicated to copy

PEARL Documentation PaSe 55

(data bases), can be copied with copy (streams) or else make nosense to copy (symbols, blocks).

For copying structures, there are currently two functions. Theone you are most likely to want is scopy which expects a single structure argument and returns anew structure with the same values init.However, the new structure will differ from the old in several important ways. First of all, copying a bound variable will result in the actual value being inserted in the new copy. When copying an unboundvariable, the new structure will receive a local variable with the samename and this variable will be installed in the slot. All variables so installed will be installed in the top level structure regardless of wherethey came from in the original. The only exception to this islexically-scoped variables. When the new structure is built, it will bebuilt within any currently open blocks and any of its unbound variables whose names match variables from the current block(s) will beidentified with those block variables. Global variables are similarlyreinstalled only if they are unbound. Adjunct variables are also installed only if they are unbound, since if they are bound their purpose will already have been served and their bound values installed mother slots referring to them.

A variation on scopy which replaces all unbound variables fromthe original with l*any* is called patternize. After (and during) therunning of these copying functions, the resulting top-level structureis kept in the special variable •currenttopcopy*.

The situation sometimes arises where you have already built astructure and have a new structure with information that should bemerged into the old one. Rather than use path to copy each relevantslot you can use smerge which expects as arguments the old structure to merge into and the new structure from which to take values.All unfrozen variables in the old structure are unbound first and thenany unbound variable whose counterpart in the new structure isbound gets replaced (not set) with this value. The old structure beingmerged into must be of the same type or an expanded version of thenew structure.

27. Miscellaneous Variations and AbbreviationsPeople very quickly get tired of typing the relatively long func

tion names that PEARL uses. As a result, a large number of abbreviations and macros have been included in PEARL We recommend thatthe shortest ones be used primarily at the top level, since they areeasily subject to typographic errors. Most the abbreviations are mcreate and are summarized by the following table:

The function or atom May be abbreviated:create prindividual indpattern patexpanded expfunction fn

Thus, (crpat ....^isequivalent to (create pattern ....).In addition, a large number of macros for popular combinations

of functions are included:

PEARL Documentation

The s-expression:(create base ...)

(create individual...)

(create expanded...)

(create pattern...)

(create function...)

Page 56

Is expanded into by the macro:fob ...)(base ...)(ci ...)(individual ...)find ...)(ce ...)(expanded ...)(pexp ...)(cp ...)(pattern ...)(pat ...)(cf ...)(pfunction ...)(fn ...)

(insertdb (create ...) nil) (dbcreate ...)(dbcr ...)(inlineereate ...)ffetchcreate ...)(inlinefetchcreate ...)(firstfetch ...)

(vp ...)(fp ...)

'(quote .(create ...))(fetch (create ...) nil)•(fetch (quote .(create ...)) nil)(nextitem (fetch ...))

!valprint ...)fullprint ...)

(pexp andpfunction are so named to avoid conflict with the exponential function exp and the function quoting function function.)

The automatic setq feature of create that causes an atom to bebound to the item created is available throughout create. In all cases,the special variable «lastcreated* is set to the item. In addition:

This combination:(create base X...(create base XY ...(create expanded XY...(create expanded XY Z ...!create individual X ...create individual X Y ...

!create individual X X ...create pattern X...

!create pattern X Y ...create pattern XX ...

Causes this atom to be set:X

YY

Z(none)Y(none, the second Xis ignored)(none)Y(none, the second Xis ignored)

When creating an object, wherever a recursive call to create isimplied by a structure in a slot of type structure, you may start withone of the types individual, pattern, base, expanded, function tochange the type ofobject being created. Whenever it isn't given, thetype of the toplevel create, which is kept in the special variable•currentcreatetype* is used. For example, in

(create pattern x(a (individual y))(b (base z (si ...) ...))(c (w)))

PEARL Documentation Page 57

where a, b, and c are all slots of type structure, slot a will contain anindividual y which the attendant defaults filled in, slot b will containthe default instance of a newly created type z, and slot c will containa pattern w with ?•any* as defaults.

Since each Usp stores its functions in a different place, PEARLincludes a macro aliasdef which expects the names of an new and aold function name and copies the function definition of the old one tothe new one. In the case of Usps which store the function definitionon the property list, aliasdef requires a third argument which is thename of the property that the definition is kept under.

28. Low Level Access Functions.

There are a large number of functions for setting and accessingthe various part ofstructures, symbols, and data bases which are primarily intended for the use of PEARL. In general, the access functions are called get... where "..." is the name ofthe information aboutthe structure. The functions which change information are calledput... It is not generally safe to use the put... functions but theget...functions can sometimes be useful to the user. For a complete list ofthe functions, see the index. If you don't recognize the function byname, you don't need it so we don't bother to further documentthem. Since most of them expect a slot number, it is useful to knowabout the macro numberofslot which requires the name of a slot andthe definition of a structure (which can be accessed with defatom ord:<structurename>.) and returns the corresponding slot number.

PEARL Documentation Page 58

29. Appendixof UCI lisp functions added to fYanz PEARLSince PEARL was originally written in UCI Usp, there are many

functions from UCI Lisp that it needed. We also wrote others to moveour other programs. The number is too great to document each one.If the function is described with an equal sign, as in ' fn = other" thenthe function definition of the Franz Usp function other has been putunder fn. Thus it might not behave quite the same as in UCI Lisp. Ifno equivalence is given, it was written from scratch which is slightlymore likely to mimic UCI Usp. In this case, see the UCI Lisp manualfor details.

The functions used for the PEARL top level loop in the Franz Uspversion plus changes to the fixit debugger and the trace package arebriefly described here also.

The Franz Usp version of PEARL is normally loaded with both thefixit debugger and the trace package already loaded. This is done toavoid getting the versions which do not know how to print PEARL objects. In addition, the Fixit debugger is attached to all availablehooks for going into the break package, since it is much more similarto the UCI Usp break package than the standard Franz Lisp breakpackage is. Both the debugger and trace package use the functionbreakprintfn to print values. The msg function uses the functionmsgprintfnto print values. Either canbe bound to whatever functionyou wish. To disengage the Fixit debugger, read the Franz manualchapter on exception handling. See Note 4 below for more onfeatures added to the Fixit debugger.

Atoms and Variables:•dskin* —special variable —initial value: t. See Note 1 below.•file* —special variable —initial value: nil. Usedby dskin

and function definition functions,•invisible* —special atom - not printed by dskin if returned

by a value when it is evaluated.

Functions:•append = append(breakprintfnvalue lmar rmar) - used by trace and debug.•dif =diff•eval = eval•great = greaterp•less = lessp•max= max(msgprintfn value lmar rmar) —used by msg.•nconc = nconc

•plus = plus•times = timesiaddprop 'id 'value 'prop)allsymitemorpair) - fexpr .,«,,. .

(apply* 'fen 'ergs) —macro - This is provided to act like UCI usp sapplytf. The asterisk is usedbecause of the special meaningof§ in Franz Usp. Unlike FranzUsp'sfuncall andapply, this doeswhat you would expectwith macros!

atcat = concat(boundp 'item)clrbfi = drain

PEARL Documentation Page 59

consp = dtpr!de fcnname arglist. &rest body) - macro - See Note 2 below,debug-replace-function-name 'cmd 'frame) —Used by the modified

Fixit debugger to handle the "> newfcnname" facility,(defp 'to 'from [prop]) - macro - Ignores prop and just

copies the function definition.(defv var val) - fexpr!df fcnname arglist fcrest body) - macro —See Note 2 below,dm fcnname arglist &rest body) —macro - See Note 2 below,

(dremove 'elmt *1)(drm char lambda) - macro —See Note 2 below,(dskin filenamel filename2 ....) - See Note 1below,(dskinl '•file*)(dskin2 'port)(dsm char lambda) —macro —See Note 2 below,(enter 'v '1)(every 'fen 'args) - macro —Potential problemwhencompiled,expandmacro = macroexpand!funl torest body) - macro- Expands into (function (lambda ...)).ge *x) —macro

(gensyml 'ident 'val)gt=>(initsym atomorpairl...) —fexpr!intersection 'setl 'set2) %islambda *fcn) - Is fen a lambda (expr)?

(le *x) —macro(length **u*)lineread = readl (below)(litatom *x) - macrolt=<

mapcl = mapcarmemb = member(msg ...) —macro —Some features may be missing. The function

used to print is msgprintfn, initially bound to(or (eq **invisible*...)

(patom (valform...)))(nconcl 'l'elmt)(nequal 'argl *arg2)(newsym atom) —fexprnoduples = union (below)(nth '1 'num)(oldsym atomorpair) —fexpr(pearl-break-err-handler) - Should be tied to ER%tpl if you want the

standard Franz Usp break (not much of a) package.Same as standard Franz Usp break-err-handler exceptthat it uses the function breakprintfn,

(pearl-top-level) - The PEARL top level loop,(pearl-top-level-init) - The initial function called when PEARL starts up.

This is the code that reads in the init files and sets any unsetPEARL parameters,

peekc = tyipeek(pop q) —macro(push var 'val) —macro(readl ['flag]) - fexpr

PEARL Documentation Pa8e 60

(readJl 'flag)remove = delete(remprops 'item 'proplist)(remsym atomorpairlist) —fexpr(save fcnname) - fexpr - Saves function or macro definition under

the property olddef. Savesmacro character definitionsunder oldmacro.

(selectq...) —macro(some 'fen'list)- macro - Potential problem when compiled,(sprint 'item ['lmar ['rmar]]) - See Note 3below,(subset 'fen 'list) - macro(timer (defun timer fexpr (request)!?(unbound) —macro(union 'listl ['Ust2 ...])(unsave fcnname) —fexpr —See save.

Note 1: Asimplified but extended imitation of the UCI Usp function dskin is provided in PEARL. It is an nlambda which requires thefile extensions to be provided. There is a special variable ♦dskin*which controls whether the expression read in is printed and/orwhether the result of evaluating it is printed.

•dskin* = nil means neither•dskin* as t means result only•dskin* = 'name means the name of the variable in setq or the name

of the function in de, df, dm, dsm, drm, defmacro,defun, or def or the name of the type in create.

•dskin* = 'both means both t and 'name.

The default value of *dskin* is t.File names are always printed before they are opened. The print

function used for values is the current function definition ofdskprintfn. The default function definition inPEARL is:

(de dskprintfn (*printval*)(cond ((atom *printval*) (patom *printval*))

( t (print (valform *printval*)))))

Note 2: For better compatibility with UCI Lisp, PEARL containsmacros for the function and read macro definition functions de. df.dm. dsm. anddrm. They have been defined to save the old definitionsautomatically and to return (fcnname Redefined) when this is thecase. De, df, and dm save the old definition under the property 'olddef. Dsm and drm save the old definition under the property 'oldmac-ro. (The current definition of a readmacro is kept byFranz under theproperty 'macro.) If the function definition is read in by dskin, thenthe current file name which is in the special variable •file* is putunder the property 'sourcefile.

Note 3: Afunction similar to the UCI Usp sprint is included, including the printmacro facility and the optional second argumentsaying which column to start in. In addition, there is an optional thirdargument saying which column totry not togo beyond (that is a rightmargin). Aslight addition has been made to the printmacro feature(feature 1 below). During sprinting, if the atom in the function position in a list has the printmacro property one of four things will happen during sprinting:

PEARL Documentation Page 61

1. If the printmacro property value is a string and the item to beprinted has a nil car, then the string will be printed instead ofthe item.

2. If the printmacro property value is a string and the item to beprinted has two items in it, then the string will be printed followed immediately by the cadr of the item.

3. If the printmacro property value is a string but the item to beprinted is longer than two elements, then it will be sprinted inthe normal fashion (i.e., the printmacro will be ignored).

4. Otherwise, the printmacro property value will be applied to therest of the arguments. It should be a function which expectsthree arguments, the item to be printed, a left column to start inand a right column to try not to go beyond. Agood default valuefor the right column argument seems to be zero. If the functionunder the printmacro property returns nil, then sprint assumesthat it decided not to print the item and prints it in the usualway.

Note 4: The Fixit debugger now accepts a command of the form> newname whenever either an undefined function or unbound variable error occurs. As in UCI Usp, newname is not evaluated in thecase of an undefined function but is evaluated in the case of an unbound variable. Note that the blank is required (unlike UCI Lisp).This is not guaranteed to work if you move around the stack first.

PEARL Documentation Page 62

30. Appendix of Franz lisp functions addedto UCI lisp PEARLThe following is a summary of the functions added to the UCI Usp

version of PEARL to make it compatible with Franz Usp. Where thedetails are not obvious, see the Franz Usp manual. Note: Most macros listed in the index which are labelled with asterisks are not available in UCI Usp PEARL, since the implementor must specifically request that they stick around. *

Dskin, the break package, and msg have been changed to use thefunctions dskprintfn. breakprintfn. msgprintfnfor printing.(addtoaddress 'n 'address) - expr - Used by cxr and

rplacx. Written in LAP code,(apply* 'fen 'args) —macro - Equivalent to apply#.(buildalist...) — expr — Used by defmacro.fcombineskels ...) - expr - Used byquasiquote.(convert...) — expr — Used by defmacro.(cxr 'index 'hunk) - expr - Ahunk is a block of memory. Provides

random access to a single cell of a hunk. (Usesaddtoaddress and even.)

(defmacro macroname arglist body) —macro —Defmacro providesa slightly more intelligent macro facility. Body isprocessed to look for occurrences of the arguments inarglist which are replaced with the appropriate formof ca..r. If an argument is preceded by Screst,then it gets the list of the rest of the arguments.The Franz Lisp version has many more features not includedin the PEARL version,

(even 'x) —expr —Is * even? Used by cxr andrplacx to determine which half of a cons-cell to use.

(isconst...) —expr - Used by quasiquote.(makhunk 'size) - expr - Calls the UCI Lisp function getblk,

requesting a block of memory which is half of size, sinceeach piece of a UCI Usp block of core is a cons-cell,

(msg ...) —fexpr - Modified to use msgprintfn to printvalues of evaluated elements of the print list,

(pearl-top-level) - the PEARL top level loop.(pearl-top-level-init) - The initial function called when PEARL starts up.(rplacx 'index 'hunk 'val) - expr - Provides random access storage into

a block ofmemory. (Uses addtoaddress and even.)(quasiquote 'skel) —expr - called by the quasi-quote readmacro

character backquote '. Equivalent to the quasiquotefunctions defined in Charniak[2] with different invokingcharacters to match those of Franz Usp.Unquote is comma ",M and splice-unquote is ",©".Uses combineskels and isconst.

PEARL Documentation Page 63

31. Bibliography[1] Bobrow, D., and Winograd. T. "An Overview of KRL, a Knowledge

Representation Language." Cognitive Science 1:1 (1977).12J Charniak, E.. Riesbeck, CL, and McDermott, D. Artificixil Intelli

gence Programming. Hillsdale, New Jersey: Lawrence ErlbaumAssociates, 1980.

[3] Faletti, J., and Wilensky, R. "The Implementation of PEARL- APackage for Efficient Access to Representations In lisp", forthcoming ERLtechnical report, UCB.

[4] Greiner, R.. andLenat, D. "A Representation Language Language.,,In Proc. First NCAI. Stanford, CA, August. 1980, 165-169.

[5] Roberts, I., and Goldstein, R. "NUDGE, A Knowledge-BasedScheduling Program." In Proc. IJCAI-77. Cambridge, MA, August.1977. 257-263.

[6] Schank, R. Conceptual Information Processing. Amsterdam:North Holland. 1975.

[7] Wilensky, R. "Understanding Goal-Based Stories". Technical Report 140, Computer Science Department, Yale University, NewHaven, CT. September 1978.

[8] Wilensky, R. "Meta-Planning: Representing and Using Knowledgeabout Planning in Problem Solving and Natural Language Understanding." Cognitive Science 5:3 (1981).

PEARL Documentation Page 64

32. Index of Global Variables and Functions With Their ArgumentsAll functions are exprs (or lexprs) unless otherwise listed. Functions

with one or more asterisks for a page number are not documented other thanin this index because they were not actually intended for use by the PEARLuser. A single asterisk * means it is primarily intended for use by PEARL butmight be useful and will generally work right. A double asterisk •• means itwill generally only work within PEARL's code, since it expects certain externalprog variables to exist and be set correctly. A triple asterisk ••• means it isdangerous to use. Note that it is dangerous to redefine any functions in thislist, although it should be all right to redefine any macros.♦activedbnames* - special variable - initial value: nil 409any9conscell9 - special variable - value: '(•any* . ^pearlunbound*) ♦9availablesi2eB9—special variable —value: 39

((-1. .1.)(0..1.)(1..1.)(2..3.)(3..7.)(4. . 13.) (5. . 29.) (8. . 61.) (7.. 127.) ....

Franz Lisp:... (8.. 127.) (9.. 127.) (10.. 127.)(11. . 127.) (12. . 127.) (13. . 127.))

UCI lisp: ... (8. . 251.) (9.. 509.) (10. . 1021.)(11. . 2039.) (12. . 4093.) (13.. 8191.))

♦blockstack* - special variable - initial value: nil 48•currentcreatetype* - special variable - initial value: base 569currentpearlstructure9 —special variable —initial value: nil 46•currentstructure9 — special variable - initial value: nil 46•currenttopcopy* - special variable - initial value: <UNB0UND> 559currenttopcreated9 - special variable - initial value: (nilstruct) 8

db - special variable - default initial value: <UKB0UKD> 33•db» — special variable - default value: the •meindb» database 12•dbladze* - special variable - default initial value: 29 399db2sdze9—special variable - default initial value: 127 39•done* —special atom 35

•fail* - special atom 35•file* —special variable —initial value: nil 60•flrstartup9 —special variable - initial value: t 53•function-stream:9 —special atom 13•globallist* —special variable - initial value: nil 45

•history* - special variable - value: command history hunk 51•historynumber* —special variable - initial value: 0 52•historysize* - special variable - default value: 64 51•histval* —special variable - value: value history hunk 51•invisible9 — special atom 53

•lastcreated* —special variable - initial value: (nilstruct) 89lastsymbolnum9 - special variable - initial value: -1 ••maindb* —special variable —default value: the main data base 11•matchunboundsresult9 —special variable - initial value: nil 44*ordinalnames* —special variable - initial value: nil 31

•pathlocal9 - special variable - initial value: <UNB0UND> 33•pathtop* - special variable - initial value: <UNB0UND> 33^earlprompt* —special variable —default value: "pearl> " 3, 4•pearlunbound9 —special atom 45•printhistorynumber9 - special variable - initial value: nil 53

•readlinechanged9 —special variable —initial value: nil 53•runaddpredhooks9 - special variable - initial value: t 34•runaddsethooks* - special variable - initial value: t 34•runallbasehooks9 —special variable —initial value: t 33•runallslothooks9 — special variable - initial value: t 33

•runapplyhooks9 - special variable - initial value: t 34•runclearhooks9 —special variable —initial value: t 34

PEARL Documentation

•rundelpredhooks* - specialvariable - initial value: t•rundelsethooks* —special variable —initial value: t•runexpandedhooks9 —specialvariable —initial value: t9runfetchhooks9 —special variable —initial value: t•rungethookhooks* - special variable - initial value: t•rungethooks* —specialvariable - initialvalue: t•mngetpredhooks* —special variable —initial value: t•runindbhooks9 - special variable - initial value: t•mnindividualhooks9 - special variable - initial value: t•runinsertdbhooks9 - special variable - initial value:t•ronmatchhooks9 - special variable - initial value: t•runnextequalhooks9 - specialvariable —initialvalue: t•mnnextitemhooks* —special variable - initial value: t•runpatternhooks* - special variable - initial value: t•ronputhooks9 - special variable - initial value: t9runremovedbhooks9—special variable - initial value:t•runsmergehooks9 - special variable - initial value: t^unstrequalhooka9 - special variable - initial value: t

9stream9 —special atom♦stream:*—special atom•toplevelp* - special variable - initial value: <UNBOUND>•unhashablevalues*- special variable —initial value:

(0unbound •pearlunbound* nilsym (nilstruct))•use9 —special atom•usealiases* —specialvariable —initial value: t♦warn* —special variable- initial value: t•zero-ordinal-value* - special variable - initial value: 0

I — splicing macroS — splicing macro= — read macro

? — read macro

(addalist 'var 'inst) - macro(addbasehook 'ccnscell 'item) - macro(addhistory 'line)(addpredpath 'item 'path 'pred)(addsetpath "item 'path 'value)

(addtoexpanadonlists) - macro(adjvarset 'var 'val) —macro(allocdef numofslots) —macro(allocval numofslots) —macro(applypath 'fen 'item 'path)

(base name [storage] slotl...) - macro(basicmatch 'iteml 'item2)(block [blockname] varlist) - fexpr(blockatom 'symbol)(blockp 'potblock)

(breakprintfn '•printval*)(builddbnewdb [olddb]) - fexpr(buildintvalue 'intval 'bppset) - macro(buildalot) —macro(buildstructvalue 'structdesc) - macro(buildsymbolvalue 'symname) —macro(buildvalue 'value 'typenum 'ppset)

(cb name [storage] slotl...) - macro(ce basename newname [storage] slotl...) —macro(cf name [storage] slotl...) - macro(chcckandrunbasehooks2 'fen 'iteml 'item2)- macro(checkandrunslothooks2 'fen 'hooks 'vail Val2 'iteml *item2)

34

34

34

34

34

34

34

34

34

34

34

34

34

34

34

34

34

34

1313

35

51

17

31

52

52

28

16

10

10

••

10

56

46

47

46

30

Page 65

58,5938

5656

56

— macro

PEARL Documentation

(checkrunhandlebasehooksl fen 'runhooksatom) - macro(checkrunhandleslothooksl 'fen 'runhooksatom) - macro(ci basename [storage] slotl ...) - macro

(cleardb ['db])(cleardb 1 'db)(cleardbactive 'db) - macro(clearhashandformat 'slotnum 'defblock) - macro(clearpath 'item 'path)

(compatible 'slotnum 'iteml 'item2) - macro(connectdb 'newdb 'olddb)(consistentvalue 'val "predlist 'typenum 'item)- macro(constructvalue) —macro(convertpreds 'pred)(copy 'list)(copypatternslot) —macro(copyslice) —macro(copyslot 'nameblock) —macro

(cp basename [storage] slotl...) —macro(cr selector ...) —fexpr(create selector ...) - fexpr(createbase 'newname 'slots)(createexpanded 'oldname 'newname 'slots)(createfunction 'fcnname 'slots)(createindividual 'basename 'slots)(createpattern 'basename 'slots)

(databasep 'potdb)(dbcr selector ...) —macro(dbcreate selector ...) —macro(debugprint 'item)(defatom 'symbol)

(defaultfortype 'typenum) - macro(definitionp 'potdef)(delpredpath 'item 'path 'pred)(delsetpath 'item 'path 'value)(disguisedas 'filler 'struct ['db])(disguisedasl 'filler 'struct 'db)

(dobasehooks2< fen 'runhookatom) - macro(dobasehooks2> fen 'runhookatom) - macro(doslothooks2< fen 'runhookatom) - macro(doslothooks2> fen 'runhookatom) - macro(dskprintfn '•printval*)

(endallblocks)(endanybiocks blockname) - fexpr(endblock [blockname]) - fexpr(enforcetype 'value 'typenum)(equalvalue 'xval 'yval 'typenum) - macro(evalfcn 'item)

(exccutehookl fen value item defblock) - macro(exccutehook2 fen vailval2 iteml item2 defblock result) - macro(expanded basename newname [storage] slotl...) - macro(expandedfetch 'item [*db])(expandedfetchl 'item 'db)

(fcnslot) - macro(fetch 'item ['db])(fetchl ftem 'db)(fetchcreate selector .) —macro

♦•

•♦

56

39

39•

10

t

54••

•♦

••

56

55

5

Page 66

30

56

12,5621

7

30

10

10

29

29

••

•♦

••

60

48

4B

47•

51

••

••

56

42

42

•♦

12,4312,4314.56

PEARL Documentation Page 67

(fillbaseslot) - macro ••(flllinl fen 'value 'item 'defblock) 33(flllin2 fen 'vail 'val2 'iteml 'item2 'defblock 'result) 33(fillindivslot) —macro ••

(findnextblockstart) —macro "(findslotnum) - macro "(flndstructsymbolpair 'defblock 'symbol) —macro "(nrstfetch pattern) —macro 14, 56(fn name [storage] slotl...) - macro 56

(followpath 'item 'path) •(for val 'init 'final Aorest 'body) —macro 54(foreach 'stream fen) — fexpr 54(fp 'item ['lmar ['rmar]]) 56(freezebindings 'struct) 48(freezeblock 'blockname) 49(freezestruct 'struct) 49

(fullform Item) 20(fullprint 'item ['lmar ['rmar]]) 20, 37(fullprintl 'item 'lmar 'rmar) 20, 37(fullalotform) —macro "

(getalist 'inst) —macro 57(getalistcp 'inst) —macro 57(getbasehooks 'defblock) —macro 57(getdbl 'db) - macro 57(getdb2 'db) - macro 57(getdbactive 'db) —macro 57(getdbchildren 'db) —macro 57(getdbname 'db) —macro 57(getdbparent 'db) —macro 57(getdefaultinst 'defblock) 57(getdefinition 'valblock) 57(getenforce 'slotnum 'defblock) —macro 57(getexpansionlist 'defblock) —macro 57(getformatinfo 'slotnum 'defblock) —macro 57(gethash9 'slotnum 'defblock) —macro 57(gethash99 'slotnum 'defblock) —macro 57(gethashl 'numl 'dbl) —macro 57(gethash2 'numl 'num2 'db2) - macro 57(gethash3 'numl 'num2 'num3 'db2) - macro 57(gethash: 'slotnum 'defblock) —macro 57(gethash:: 'slotnum 'defblock) - macro 57(gethash< 'slotnum 'defblock) —macro 57(gethash> 'slotnum 'defblock) —macro 57(gethashalias 'defblock) —macro 57(gethashinfo 'slotnum 'defblock) - macro 57(gethashvalue 'slotnum 'item 'defblock) 9

(gethookpath 'item 'path) 10(getisa 'valblock) —macro 57(getpath 'item 'path) 10(getpname 'defblock) —macro 57(getppset 'slotnum 'defblock) - macro 57(getpred 'slotnum 'inst) —macro 57(getpredpath 'item 'path) 10

(getsinglevalue 'slotnum 'item) *(getslot 'slotnum Inst) - macro 57(getslothooks 'slotnum 'inst) - macro 57(getslotname 'slotnum 'defblock) - macro 57(getslottype 'slotnum 'defblock) - macro 57(getstructlength 'defblock) - macro 57

PEARLDocumentation P«g« 68

(getstructorsymnum 'strsym) —macro 57

(getsymbol 'symname) 4(getsymbolpname 'symbolitem) —macro 57(getuniquenum 'defblock) —macro 57(getvalue 'slotnum 'inst) 57(getvarandvalue 'slotnum 'inst 'var) 57(getvarval 'slotnum Inst) —macro 57

(•global9 varname) —fexpr 46(global variable) —fexpr 45(globalp 'variable) 45(handlehookresult 'oldval 'newval) - macro "(haahablevalue 'slotnum 'item 'defblock) - macro "(hashslot) — macro "

(hidden 'command) —macro 35(higheroreq 'iteml *item2) —macro •(history ['num]) 53(ind basename [storage] slotl...) - macro 56(indb 'item ['db]) 14(indbl 'item 'db) 14(individual basename [storage] slotl...) —macro 56

(inheritvalue 'structdef) —macro "(inlinecreate selector ...) —macro 14, 56(mlinefetchcreate selector ...) —macro 14, 56(insertdb 'item ['db]) 12(insertdbl 'item 'db) 12

(insidecreate selector ...) — fexpr "(inatidefetch patdef expdefs) — macro "(insidefetcheverywhere patdef expdefs) —macro "(insidepattemize 'item) "(jnstidescopy 'item) ••(installadjunct 'adjunctvar) ~ macro(installglobal 'globalvar) —macro(installvar 'varname) - macro

(instatom 'symbol) 7(isa 'iteml 'name) 42(isanexpanded 'iteml 'item2) 42(islambda fcnname) 9

(match 'iteml 'item2) 46(msgprintfn ,9printval9) 58. 62(newnum) —macro •(nextequal 'stream) 46(nextitem 'stream) 13(noalias) —macro ••

(nullstruct 'item) 42(nullsym 'item) 42(numberofslot 'slotname 'defblock) —macro 57(onesymbol) —macro "(ordetom 'symbol) 31(ordinal name vallist) —fexpr 30

(pat basename [storage] slotl...) —macro 56(path fen 'item 'pathlist ['val]) - macro 9(pattern basename [storage] slotl...) —macro 56(patternize 'item) —macro 55(patternizeslot) —macro "(pboundp 'a) 45

PEARL Documentation Pege 69

(pearlprintfn ,9printval9) 3, 4(pexp basename newname [storage] slotl...) —macro 56(pfunction name [storage] slotl ...) —macro 56(pname 'item) 4(ppsetform 'slotval 'ppsetname) 9

(prefix 'iteml 'item2) 52(prefixcommandhistory) 9(prefixcommandvalue) 9(printdb ['db]) 21(printdb 1 'db) 21(psymbolp 'potsymbol) 30(punbound) 45

(punboundatomp 'yyy) 9(putalist 'alist 'inst) —macro 9(putalistcp 'alist 'inst) —macro *(putbasehooks 'booklist 'defblock) —macro *(putdbl 'dbl 'db) - macro •••(putdb2 'db2 *db) - macro •••(putdbchildren 'childlist 'db) - macro 9"(putdbname 'name 'db) —macro 9(putdbparent 'parent 'db) —macro 9"(putdef 'defblock 'valblock) —macro 9"(putdefaultinst 'valblock 'defblock) - macro 9"(putenforce 'slotnum 'defblock) —macro *"(putexpansionlist 'explist 'defblock) —macro 9"(putformatinfo 'slotnum 'hashnum 'defblock) —macro 9"(puthash* 'slotnum 'defblock) —macro ***(puthash** 'slotnum 'defblock) —macro *"(puthash 1 'numl 'dbl 'item) —macro 9(puthash2 'numl 'num2 'db2 'item) —macro *(puthash3 'numl 'num2 'numS 'db2 'item) - macro *(puthash: 'slotnum 'defblock) —macro 9"(puthash:: 'slotnum 'defblock) —macro "♦

(puthash< 'slotnum 'defblock) —macro 9"(puthash> 'slotnum 'defblock) — macro 9"(puthashalias 'hashnum 'defblock) —macro •••(puthashinfo 'slotnum 'hashnum 'defblock) —macro •••(putisa 'isa 'valblock) — macro •••

(putpath 'item 'path 'value) 10(putpname 'name 'defblock) —macro ♦••

(putppset 'slotnum 'setname 'defblock) —macro *(putpred 'slotnum 'value 'inst) - macro *(putslot 'slotnum 'value 'inst) —macro *"(putslothooks 'slotnum 'slothooklist 'inst) —macro *(putslotname 'slotnum 'slotname 'defblock) —macro "•(putslottype 'slotnum 'typenum 'defblock) —macro 9"(putstructlength 'size 'defblock) —macro •"(putsymbolpname 'name 'block) —macro 9"(putuniquenum 'num 'defblock) —macro •••(putvarval 'slotnum 'value 'inst) —macro *"(reallitatom 'potatom) 9

(releasedb 'db) 38(removedb 'item ['db]) 12(removedbl 'item 'db) 12(removeslot) — macro "(revassq 'value 'alist) 9(runbasehooksl fen 'item) 33(runbasehooks2 fen 'iteml 'item2 'result) 33(runslothooksl fen 'item 'slotname 'value) 33(runslothooks2 fen 'iteml *item2 'slotname 'vail 'val2) 33

PEARL Documentation Pft*e TO

(savecontinue 'directory 'name) S3(savefresh 'directory 'name) 63(savepearl) #(scopy 'item) —macro 5^(scopyslot) —macro •t

(setblock blockname) —fexpr 48

(setdbactive 'db) - macro •••(setdbsize 'poweroftwo) 39(setv var 'val 'environment) —fexpr 47(slotequal 'slotnum 'iteml *item2) 9(slotnametonumber 'slotname 'defblock) —macro "(smerge 'build 'from) 85

(standardfetch 'Hem ['db]) 43(standardfetchl 'item 'db) 43(standardmatch 'iteml *item2) 46(streamp 'potstream) 30(streamtolist 'stream) 14

(strequal 'iteml 1tem2) 46(structurenamep 'potname) 30(structure? 'potstruct) 30(symatom 'symbol) 4(symbol name 1 name2 ...) —fexpr 4(symbole 'symname) 4(symbomamep 'potname) 30

(thawbindings 'struct) 49(thawblock 'blockname) 49(thawstruct 'struct) 49(unbind globalvar) —fexpr 45(unbindvars 'structure) —macro 48(unboundatomp 'yyy) •

(valform 'item) 20(valprint 'item ['lmar ['rmar]]) 20(valprintl 'item 'lmar) 20(valslotform) - macro ••

(valueof 'var 'struct) 17

(^ar9 varname) - fexpr 46(varset 'var 'val) —macro #(varvalue var 'val) —fexpr IV(visible 'command) —macro 35(vp 'item ['lmar ['rmar]]) 56(while 'val Merest 'body) - macro 54

PEARL Documentation

33. Concept Index

abbreviationsaccessing slots of structuresaccessing structure default instancesaccessing structure definitionsaccessing symbols

adding slots to structuresadding to the data baseadjunct variablesaffecting forced aliasing (~)ako's (expanded structures)

aliasing of commandsaliasing in hashingampersand (&) hashingand, in predicatesanti-aliasing in hashing (<)•any*automatic storing of structures

base hooksbasesblocksbuilding structuresbuilding upon data bases

changing slots of structuresclearing data basescolon (:) hashingcolon-colon (::) hashing

command aliasingcommand historycommand history, printingcompatibility functions (UCI, Franz)

controlling running of hookscontrolling results with hookscontrolling unbinding of variablesconverting from internal formcopy redefinedcopying structures

creating data basescreating patternscreating base structurescreating individual structurescreating symbols

data basesdata bases,data bases,data bases,data bases,data bases,data bases,data bases,data bases,data bases,data bases,

building uponclearingcreatingfetching fromfreeinginserting intoprintingreleasingremoving fromsetting size of

debuggingdebugging printdeclaring global variables

55-56

8-10

7

7

4

40

1230

27

40-42

51

27

26

28

27

15

6,56

32-375

47-46

5

3B, 39

8

39

23

24

51

51-53

53

58-62

33-34

35

48-49

20

54

55

38,3915-16

5

6

4

11

39

30

3012, 19, 25, 42. 43, 4640

12

2140

12

39

21

21

45

Page 71

PEARL Documentation

default fetch function 43

default instance for a structure 15

default instance, accessing 7

default match function 46

default printing functions 20, 56, 60-61, 62

default values for slots 14-15

defaults, inherited 41-42

defining structures 5

defining symbols 4

definitions of structures, accessing 7

deleting from the data base 12

demons (hooks) 32-37

disguising in path 10-11

disguising in predicates 29

don't-care matching variable 15

double-colon (::) hashing 24

double-star (") hashing 24

dumping PEARLfor later 53

efficiency despite variables 30

enumerated (ordinal) types 30

environment for variable evaluation 46-47

environment, top level 51-53

environments, in hooks 33

equality of structures 46

equivalences of functions (UCI-Franz) 58-62

error messages 21

evaluating function structures 51

evaluating in create 22

expanded structures 40

expanded structures, fetching 42

feedback, sending 21

fetch, standard 46

fetching expanded structures 42

fetching from the data base 12, 19, 25, 42, 43. 46

fetching with equality (not matching) 46

filling in special forms (in hooks) 33

for loop 54

forced aliasing (>) 26

forest of data bases 39-40

freeing data bases 40

freezing variables 48-49

function equivalences (UCI-Franz) 56-62

function structures 49-51

function structures, evaluating 51

getting symbols 4

global variables 45

greater-than (>) hashing 20

hash aliasing (&) 26

hash marking 17,23-27

hashing problems 18

hnphing with variables 30

hiding functions from hooks 35

hierarchy of structures 40

history mechanism 51-3

history number, printing in prompthooks

53

32-37

Page 72

PEARL Documentation

hooks, affecting result with 35

hooks, controlling running of 33-34

hooks, hiding functions from 35

hooks, making functions visible to 35

hooks, multi-argument 28

hooks, running 33-34

if-added functions (hooka) 32-37

indirection in path 10-11

individuals 6

inheritance in structures 41-42

(.)init.prl file 2-3

inserting in the data base 12

instances 6

integer slots 30

internal access functions 57

internal form printing 21

invisible functions to hooks 35

invisible results from functions 53

isa's (expanded structures) 40-42

less-than (<) hashing 27

lexically scoped variables 47-48

looping functions 54

low level access functions 57

macros, special 56

main data base 11

marking structures for hashing 17,23-27

match, standard 46

match, without unbinding variables 46

match-anything variable 15

matching process 44

matching two structures 43

matching unbound variables 44

matching-variables 16-17

merging structures 55

modified input line, printing 53

multi-argument matching predicates 28,32

next item in a stream 13

nilstruct(ure) 14

nilsym(bol) 14

or, in predicates 26

ordinal types 30-31

path functions 10

path indirection 10-11

pattern-matching variables 16-17

patterns 12, 15, 43

patterns in matching 43

predicates for object types 30

predicates in matching 27-29

predicates in matching, when run 44

predicates in matching, multi-argument 28

print names 4

printing PEARL objects 20

printing command history 53

printing data bases 21

printing functions 20

nrintins functions, standard 3-4, 58, 60-61. 62

Page 73

PEARL Documentation

printing history number in promptprinting modified input lineprinting warnings

processing a streampromptprompt-re ad-eval-print loopread-eval-print loopredirecting in create (! and S)releasing data basesremoving from the data base

reporting bugsretrieving from the data basereturning invisible resultsrunning hooksrunning under Franz Lisprunning under UCI Lisp

saving PEARLfor laterscalar typesshort-circuiting in createside effect setting of adjunct variablessize of data bases

slot hooks•lot names to numbersslot typesslot types, more specificslot values•lot values in hooks•lot values in predicates

special forms in hooksspecial forms in predicatesspecial forms, filling inspecial macrosstandard fetch functionstandard match function

star (♦) hashingstar-star (") hashing(.)start.prl filestartup filesstoring structures in the data basestoring of structures in atomsstreams

structure equalitystructure matchingstructure predicatesstructure slots, further typingstructured escapes to Lispstructures

structures, copyingstructures, expandedstructures, functionstructures, merging

symbolstesting for nilstructtesting for nilsymtesting for object typesthawing variables

53

53

17

13

3-4

2-3, 512-3,5122

40

12

21

12

53

33-34

2

3

53

30

22

30

39

32-37

57

6

30

6-10

32

28

3228

33

58

4346

17,2324

2-3

2-3

12

8, 5613

46

44-45

28-29

30

49-51

5

55

40

49-51

55

4

42

42

30

48-49

Page 74

PEARL Documentation

top level looptop level loop functionstriple (") hashingtype tests for objectstypes in structure slots

2-3, 5159,6224

30

31-2

unbinding global variables by match (lack of) 45unbinding global variables by user 45unbinding local variables by match 45-6unbinding local variables by user 46unbinding of variables, controlling 46-49up-arrow (**) hashing 27

values of variables 17,46

values of variables, setting 47

variables in hooks 32

variables in predicates 28

variables with hashing 30

variable, accessing values 17,46variables, adjunct 30

variables, controlling unbinding 48-49

variables, freezing 48-49

variables, global 45

variables, lexically scoped 47-48

variable, setting values 47

variables, side effects 30

variables, thawing 48-49

variables, unbinding 46

visible functions to hooks 35

warnings 17

while loop 54

Page 75

PEARL Documentation Update Page 76

Update of ChangesThrough

PEARL3.8April 1983

1. Introduction

otaJ*"8- aPPendbc describes the changes that have been made toMSAKL since the original manual was produced. It is designed toparallel the sections of the manual so that the original index can beused to find changes.

no ci?1^ te now dktobuted with Franz lisp (starting with Opuswim - earliest version of PEARL distributed (with 38.58) wasPEARL 3.6. The current update corresponds to version 3.8. Thecurrent major and minor version numbers for PEARL are stored in thespecial variables pearlmajorversion and peartminorversion respec-

With the change in mail protocols and addition of new machinesat Berkeley, the form of addresses for bugs and suggestions havebeen simplified. Bugs, suggestions or queries should be sent toPearl-Bugs&Berkeley orucbvaxfpearl-bugs.

2. Running PEARL

PEARL is currently only maintained under Franz lisp Thecurrent version could be moved back to UCI lisp (or to other Lisps)fairly easily but has not been for lack of need, lisp Machine lisp isthe most likely lisp that PEARL will be moved to next but it has notbeen done, mostly because of conflicts in the use of the colon character and lack of access to a lisp Machine.

2.1 Under Franz UspSince PEARL is now part of Franz lisp, it should be available as

Aisr/ucb /pearl or wherever you find lisp onyour system.The .start.pearl and .init.pearl files are actually called start.prl

and init.prl and may optionally be prefixed with a dot "." and/orsuffixed with either ".o" or ".1" just as in Franz. The use of the dotprefix and of the ".o" or ".1" is preferred and fastest. Thus PEARL willread the first file found in the following sequence: .init.prl o,.init.prl.I, .init.prl, xnit.prl.o, init.prll, or init.prl and similarly forstart.prl. Franz's special variable Sldprint is lambda-bound to nilduring the reading of these two files to disable the printing of "[load•ixut.prlj".

6. Accessing Slots of Structures

Doing a "path put" on a slot containing a variable will not set thevariable. Rather it replaces the variable with the value provided.

10. Printing Structures. Symbols andOther PEARL ObjectsThe various printing functions still exist but all call a single for

matting function with various options controlled by special atoms.The principle functions are aliform which does the building of a printable list form for internal PEARL structures and allprint which callsaliform. Aliform uses the following global variables to determine

PEARL Documentation Update Page 77

what form to build:

1. •abbrevprint* - a non-ntf value causes abbreviations to be usedwhenever possible for any structure except the top level structure passed to a print function. Abbreviations are described atthe end of this section. The new funcUons abbrevform andabbrevpnnt lambda-bind this to t and then call aliform.fuUform binds this to nil.

2. nul^print* - anon-ntf value causes complete information including hooks and predicates to be given when present. FuUform(and thus fuUprint) lambda-binds this to t and calls aliform,Abbrevform binds this to nil.

Itolform lambda-binds both to nil. The default value of both is arealso nil so that the default action ofaliform when used by itself willbe like valform unless these special variables are changed. All thedefault print functions automatically use aUprint so that they can allbe changed by changes to the default values of •abbrevprint* and•fuUprint*. r

Two other special atoms which affect the behavior of all the printingfunctions are:

3. niniqueprint* - a non-ntf value causes a structure which isencountered more than once during the same top-level call to aprint function to be translated into exactly the same cons-cells.This saves on cons-cells and also makes it possible for the—form functions to handle circular structures, although sprintand thus the —print functions cannot handle the result. Sincemost people seldom have duplications within a structure, thedefault is nil (off). The assoc-list of already translated structures is stored in the special atom •uniqueprintlist*

4. "quiet* - a non-ntf value disables all printing by any of PEARL'sprint functions, providing an easy way to disable printing all atonce. There is also a function called quiet which behaves likeprogn, except that it lambda-binds •quiet• to t during the evaluation ofits arguments, providing alocal island of"quiet".The standard print functions are designed to handle any lisp

structure. Thus, they spend asignificant amount oftime determiningwhat kind of object they have been passed. For situations in whichyou know exactly what type of object you want printed, the functionsstructureform/structureprint symbolform/symbolprint andstreamform/streamprint are provided. They assume you know whatyou are doing and do not ensure that you give them the right type ofvalue. ^ 'r

Adapting PEARL to fit an improvement in Franz, the atomsshowstack-printer and trac*printer are bound to the functionspearlshowstackprintfn and pearitraceprintfn. Note the addition ofpearl" to the beginning of these. The name of breakprintfn was also

changed to pearlbreakprintfn but it is not currently lambda-bindable.

10.1. Abbreviations

As people build larger deeper structures it becomes useful tohave some of them abbreviated during printing if they are internal tothe structure being printed. When an individual (including defaultinstance) structure is created, an abbreviation atom is stored in it

PEARL Documentation Update Page 7B

Ibis abbreviation is chosen as follows:

1. If the option in create of having a structure automatically storedin an atom is used, then that atom is the one used as an abbreviation. Thus the structure created by (create individual x Pete)will be given the abbreviation Pete.

2. If that option is not used, then default instances will be given theabbreviation i:x (where x is the structure type name) and individuals at the top level will be given a name newsym-ed from thename of their type. Thus (create base x) will make a defaultinstance abbreviated i:x and the first structure created with(create individual x) willbe abbreviated xO.

3. Scopy and related functions that create new structures from oldones gensym the new structure's abbreviation from that of theold structure.

11. ErrorMessages. Bugs, andErrorHandling AbilitiesBugs, complaints and suggestions of useful features (to be added

to the current list of 30 or so things on the wish list) should be mailedby electronic mail to Pearl-Bugs@Berkeley or ucbvaxfpeari-bugs.

12. Short-Circuiting and RedirectingCreate Using !. S and AtomsIf an atom is encountered where a value-description was

expected in any type of slot, and it is bound to a value of the righttype, its value is inserted into the slot. For symbols, this is done ifthe atom is not a symbol name. For structures, the atom must evaluate to a structure. For Usp slots, it must simply be bound. For setofslots, its value is checked for being of the appropriate type, includingdepth of nesting.

Note also that a change in the internal representation has madeit possible to allow even atoms in slots of type lisp.

13. More Flexible Hash Selection

Because we have never gotten around to adding fetch functionsto take advantage of colon and colon-colon hashing and these twomethods really are not useful in normal fetching, they are currentlyignored.

For situations in which you wish to create an expanded structureand add newhashing marks to an old slot (rather than replace them),preceding new hash marks with a plus("+") will cause the old hashinginformation to be copied before processing the newhashing.

Thus, the sequence

fcb x (• a int))ice x y (a ~))fee x z (+ : a ~))(cexw(: + a -)) ; anomalous use of +

will result in:

PEARL Documentation Update Page 79

* hashing in x,no hashing in y,both * and : hashing in z, andonly*hashing in w(because of misplacement of +).

Several new hashing methods have been added to PEARL.A hashing mechanism using the label •*• has been added called

"triple-star hashing". If slots are labeled with ••• and all slots somarked are filled with useful values, then the item is hashed underthe type of structure plus the values of all these slots. During fetching, this is considered the most useful (that is. specific) hash method.

A hashing mechanism using the label && has been added called"hash focusing". It is designed for people using a data base all ofwhose entries are of the same type (not required, just common forthis application) and enables the contents of a single slot to be usedto better discriminate them. Examples ofsuch structures are "plan-fors", inference rules, or almost any other such extremely-commonbinary predicates. If a slot labeled && is found when inserting intothe database then the item is hashed as if it were the item in the slotso labeled. At fetching time, && is considered less useful than *** or•* and more useful than * or nothing.

This differs from &(hash aliasing) in that hash focusing affectshow a structure itself is inserted and fetched, while &simply affectshow structures containing this type of structure are treated. Forexample, suppose the unique numbers of A, B, and Crespectively are1, 2, and 3. Cis a symbol. Ahas one slot Xwith *and && hashing. Bhas one slotYof type symbol with • hashing. Then a structure like (A(X (B (Y C)))) will be indexed the following ways and fetcheverywhere(see below) will find it in the following order: the && method will beused first which uses the 2 and 3 from B and its C. (ignoring the 1 ofA), and also simply 2from B; the • on Auses the type of Bthus using 1and 2; it is also looked for under the 1 of Awithout using 2 or 3. If Bhad an &in its slot then the * on Ais affected by &on Bthus using 1and 3 (ignoring the 2 of B).

Thus, if you consider A. B. and C to be three levels of informationin the structure, an item can be hashed under any combinationof twoofthose levels. The normal • method uses levels 1 and 2, the aliasingSc method ignores level 2 and uses levels 1 and 3, and the new focussing && method ignores level 1 and uses levels 2 and 3. In addition,the item can be put under 1. 2 or 3 individually by various combinations of marks (l = none, 2 = :, 3 = :+&). The only unavailable combination of the three is all of them.

16. AttachingHooks to Structures (If-Added Demons)Slot hooks are now always inherited and added to, rather than

replaced. If the hooks and predicates of a slot are preceded byinstead then inheriting does not happen and hooks and predicates arereplaced.

The atoms for path hooks were misnamed in such away that youcould not use hidden and visible. Instead of *rungethooksm, and other*run...hooks9 forms, they are now *ningetpathhook8* and other*nin...pathhookB*. Note that they must be called as (XXXpath ...) andnot (path XXX...) when used with hidden and visible.

PEARL Documentation Update Page 80

17. Creating and Manipulating Multiple Data BasesThe function setdbsize can now be done at any time and will

remove all current databases before changing the size, warn the user(if •warn* is set) and recreate *maindb* with the special variable•db* pointing to it.

The function cleardb is now a local database clearer and itseffects do not extend up the database hierarchy.

10. Creating Expanded Subtypes of Previously Defined ObjectsHashing in old slots inherited by new expanded structures can

now be added to by preceding the new hash marks with plus ("+").See section 13 above.

The name of an old slot inherited by a new expanded structuremay be changed by following the new name by the old slotname preceded with an equal sign. Thus for example:

pearl> (create base X (A struct))(X (A (nilstruct)))

pearl> (create expanded X Y (B =A) (C ))(Y (B (nilstruct)) (C )))

Note that there may not be a space between the equal sign and theslot name since = is a read macro which expands =4 into (*slot* A)but leaves a single space-surrounded equal sign alone. The actualeffect is to add another name to the slot so that it can be later referenced with either name.

20. Fetching Expanded StructuresA fetching function called fetcheverywhere exists which gathers

all the buckets the object could have been hashed into and builds astream out of all of them (potentially five buckets). There iscurrently no "expanded" counterpart, since it has the potential ofreturning 5 times the-depth-of-the-hierarchy buckets.

21.2 The Matching Process

During matching, if an unbound global variable is set and thematch later fails, the value is restored to *pearlunbound*. The namesof variables that are set are saved in the special variable *global-savestack*

Formerly, there was only one match function which was used byboth standardfetch and expandedfetch and which therefore wouldmatch two structures if they were hierarchically related. This isreally inappropriate for the standard fetching, so there are now tworegular match functions, standardmatch and basicmatch, which willonly match two structures of the same type, and two expanded matchfunctions, standardexpandedmatch and basicexpandedmatch, whichwill match two structures which are related hierarchically (one abovethe other) on the slots they have in common. Streams built by standordfetch use the regular versions and and streams built by expandedfetch use the expanded versions.

There are now two functions memmatch and memstrequal whichare like memq except that they use match and strequal respectivelyinstead of eg.

PEARL Documentation Update Page 81

As of version 3.8. PEARL will now do unification of variables inpattern matching. To turn it on, call the function useunification.(Ihe current implementation precludes turning it off once it is on butthis may be remedied in later versions if we can figure out what itmeans to stop unifying.)

28. Looping and Copying FunctionsThe function scopy no longer deletes bound adjunct variables.The standard Franz function copy is no longer redefined since the

Btandard version now avoids the copying of hunks.

The functions scopy and pattemize are now exprs rather thanmacros.

The new function varreplace permanently "freezes" the values ofslots containing bound variables by replacing all bound variables in anitem with their values.

A variation on scopy called intscopy ("internal scopy") isdesigned to do the copying as if the copied item were internal toanother outer item, thus sharing its local and block variables. Itsarguments are the item to be copied and the outer item in whosescopy the copying should be done.

20. Appendix of UCI lisp functions added to Franz PEARLThe definitions of de, df, dm, drm and dsm have been modified so

that if the special variable *savedefs* is nil then old definitions offunctions are not saved. This is especially useful in compiling (and asa result, assembly and loading) since it will speed them up quite a bit.This also disables the saving of the name of the file that the definitionwas in. The variable *savedefs* is normally t which causes these macros to act as before, saving the definition, etc. If •savedefs* is nil,then they simply expand into the appropriate defun or setsyntax.The following lines should be included in a file to have this effect onlyat compile time:

(eval-when (compile)!declare (special *savedefs*))setq *savedefs* nil))

If you also want to permanently disable this feature in a lisp, thatloads ucisubset.l, simply put a (setq •savedefs* nil) in your .lisprc fileAFTER the loading of ucisubset.l.

The function remove is no longer made equivalent to Franz'sdelete so that Franz's remove can be used. Ihe functions nth, pushand pop are no longer defined by PEARL, since the new Franz versionsare better. (UCI lisp users note: This switches the arguments topush.)

32. Index of Global Variables and Functions with Their ArgumentsAll special variables in PEARL are now defined with defuar so that

fasVing in pearl,o at compile time will automatically declare themspecial again.

All the exprs whose names were of the form XXXXl where XXXXwas the name of a lexpr which was a principle function of PEARL wereeliminated (i.e„ absorbed by the other form).

"^

PEARL Documentation Update Page 82

34. Compiling Iisp+PEARL Files.

To compile a file of mixed lisp and PEARL functions with liszt,you must first load in the function definitions and special declarationsof PEARL by loading the object code. This is the file pearl.o which isnormally kept in the AisrAibAisp directory and will found automatically by load.

Thus, the foUowing should normally be included at the beginningof a PEARL file you wish to compile:

(eval-when (compile)(declare (special defmacro-for-compiling))fsetq defmacro-for-compiling t)(load 'pearl,o))

(declare (macros t))