Page Numbers: Yes First Page: 1
Heading:
April 26, 1979 6:16 PM [IVY]<KRL>document>str-basic-attach-ext
Metaphors for procedural attachment
Although there was no syntactic means to specify the distinction, the signal actions, triggers and traps of KRL-0 were clearly divisible into two classes -- demons and servants. Demons were used for data-directed process invocation. They specified a particular condition (for example, adding a new description to a filler in a perspective), and when that condition occurred, the process (A LISP expression) associated with the demon was executed.
Servants were of two somewhat different types (with a fuzzy area in-between), value producers and action producers. Value producers were associated with specific situations in which a LISP value was needed from a computation. When that situation was recognized, the system would execute the body of the servant (a LISP expression) and its value would be used. For example, a commonly used trigger was ToFill, which was associated with a slot and whose value was a unit which in KRL-1 would be described as the result of a Seek with the initial description a slot perspective with that slot as its focus. Another frequent use of servants was in the signals associated with match routines. A signal was invoked, and the corresponding action was expected to return a value of Match, NoMatch, or Unknown.
Servants could also be invoked to perform an action (like a PRINT) without returning any value. The signals referred to as "notifications" fell into this category, as did some triggers. There was no systematic notion of how the value returned by a servant was related to the context in which it appeared. It was handled on a case by case basis, both for system- defined and user-defined servants. This in fact was one of the weakest points of the documentation. In order to find out what kind of result was expected from a servant, and what would be done with it, it was often necessary to find the place in the code where a signal was generated or a trigger or trap was probed.
In KRL-1, we have attempted to fit demons and servants into a more general framework, both syntactically and semantically. The "uniformization" can be described in several steps:
Statements of how LISP values are related to enities can be separated from procedural attachment for causing their use: Servants in KRL-0 really conveyed two kinds of information. One part was procedural: "When you have the goal X to achieve, do Y". The other part was really declarative, e.g.: "The thing which fills this slot is the value of the LISP expression Z". The merging of these two aspects led to a good deal of confusion about just what servants were, and how they related to other descriptions. If a user had a procedure, for example, which would produce an English summary of a description, he or she could choose between inventing a trigger ToSummarize, or a functional SummarizedBy whose argument was a LISP expression In the first case, a summary could be produced by probing the trigger, and in the second by evaluating the argument of the functional. Both would work, but there were subtle differences in how they were inherited. If the functional form was used, then the information was inherited normally like any other description. If the trigger form was used, then the information was inherited only if a further-specification hierarchy was established. System-probed servants could only be inherited by using further specification (this was in fact one of the major motivations for including the further specification mechanism).
In KRL-1, we have separated out the two kinds of information. The declarative part appears as part of the normal description structure (in the slot descriptions and self description of a unit, embedded at any level of nesting). The procedural part is expressed in footnotes. Traps are associated with pieces of the description structure (footnotes on descriptions) and triggers with unit structure (footnotes on units and slots -- see the point on indexing, below).
Servants are just demons triggered by the setting of goals: The framework of section 2.3 makes it possible to define servants and demons in terms of goals and operations in the interpreter. A demon can be viewed as saying: "If a situtation (an event happening in the interpreter) matching the pattern X is activated, then add to the agenda an operation Y". Some examples of situations which correspond to demons in KRL-0 are: the adding of a new descriptor to a description, the adding of a new perspective to a self-description, etc. Servants can be seen as a specialized case of this. Each servant can be viewed as saying: "If a goal matching the pattern X is added to the set of active goals in the interpreter’s data structures, then add to the agenda an operation Y".
In KRL-0, it was unclear what should happen if more than one trigger or trap were associated with a situation in which a servant was needed. Since servants were expected to return values, there was a clear one-need, one-provider situation. In the KRL-1 view, any demon can provide any number of operations to be added to the agenda. In the case of servants, it is expected that the operations suggested will help towards achieving the goal which invokes them. One of the standard things which can be suggested is "Take the action X on the description form Y" where the description form is one of the declarative forms relating a result to a LISP process which will produce it (like the ToSummarize example above). However, an operation suggested by a servant does not need to be related to the goal in a simple one-one value-producing way. It is quite reasonable for a KRL knowledge base to contain a number of servants for the same goal, some of which suggest operations likely (perhaps only in a loose heuristic sense) to lead towards progress, others suggesting operations which may affect scheduling priorities, etc. etc.
Triggers, traps, and signal actions are all just different ways of cataloguing the set of responses to system events: The opening of this section proposed the idea of a global "pool" of productions. The pattern on each production specified the details of the events for which it would respond. There are reasons to provide more structure to this pool, both for the implementer and the user. The implementer does not want to have to test any more patterns than absolutely necessary for each event. The user wants to organize his or her set of productions in a way which clarifies the conceptual structure of the knowledge base. Some of them are best thought of as generally associated with a specific system event, others as "belonging" to a particular procedure, others as belonging to a specific unit, or description.
The mechanisms of KRL-0 were primarily means of cataloguing responses in appropriate places. Signals were catalogued in a two level structure, first by association with an individual process, and within that by event prototype (signal name) <more discussion later on the notion of signal path>. Triggers were catalogued by unit and slot, and within that by event prototype (the name of the individual trigger, like WhenFilled). Traps were catalogued by the description on which they were to act, and within that by event prototype.
There were problems, however, raised by the confusion between this notion of cataloging, and the content which related the LISP values resulting from actions to the structures in which the responses appeared. One example was the difficulty of having a response to a condition of several slots in a unit being filled, or any one of a set of slots. In KRL-1 terms, the pattern wanted to specify a unit and a description of its state, where that description involved not one slot, but a set of slots. Another similar example (which Paul wrestled with extensively in the cryptarithemetic program) was a situation where the pattern specifies both a specific description (in trap style) and a specific unit and slot (in trigger style). Since KRL-0 insisted on one or the other, he ended up adding traps to triggers, and triggers to traps, then removing them once they had fired, etc., etc.
From this vantage point, we can view a "simple" trigger appearing in a slot (such as (ToFill (DoStuff.....))) as expressing four separate messages:
* Cataloguing: Don’t even think about this or waste time looking at it unless you are currently setting up a goal to find a filler for an instance of this particular slot in this particular unit.
* Pattern: Do the response in every case where you are currently setting up a goal to find a filler for an instance of this particular slot in this particular unit. In KRL-0 it was impossible for the pattern to contain anything other than the information used in cataloguing. This led to the problems mentioned in the previous paragraph.
* Action: The piece of LISP code
* Use of results: Implicit in the use of name ToFill is the information that the LISP value returned by running the code will be a pointer to a unit, and a coreference descriptor to that unit is the desired form to be added to the filler.
Signal tables also combined these four in a similar way. We want to maintain the ability to combine them when that will make the knowledge base smaller and clearer, but we have added the ability to specify them separately when that is appropriate. Section 2.?? describes the ways in which responses can be associated with units, slots, and descriptions using footnotes. Section 2.??? describes the association of responses with processes, and globally with event prototypes. This includes the ability to catalog user-generated events, as well as the built-in system events. Section 3.?? discusses the implementation of all of these, using discrimination networks.
D. -- Interfacing with LISP
D.2 -- LISP’s Descriptor
(NOTE -- THIS SHOULD ALL BE CHANGED!! -- The following discussion merges the notion of a descriptor which interfaces to LISP expression, comlpete with variable binding, which we have decided to call LISP’S, and also the standard functional called ValueOf(), which means a co-reference with the value of the LISP expression which is its argument. All of the following should be rewritten to reflect this new distinction)
LispValue descriptionOfAnS-expression with
variableName-1 = description-1
variableName-2 = description-2
...
Some Examples:
LispValue ’(Modulo X base) with x = my input
base = 3
LispValue ’(Gensym)
#Tester
answer: LispValue my generator with y = "abcdefg"
z = my prefix
prefix: a String with length = whichIs LessThan 17
generator: a LispExpression
The form of the LispValue descriptor is very similar to that of a perspective in that it takes an optional arbitrary number of filler pairs of the form name = description. However this is in some sense a "pun", since the name variable must be a LISP variable name (and hence obviously can’t be an arbitrary description), and the meaning is that the description is bound to that variable when the LISP expression is evaluated. Note that if appropriate a closure for reflexive descriptors (my ... ) is taken before binding, and also that the existence of the descriptor does not in itself indicate when, if ever, the LISP form will be evaluated.
There is a potential problem involving how a LISP function returns a value indicating that it does not have a value which has meaning as a description or pointer. We have designated as special the LISP atom nihil to be the "KRL-1 nil".
D. -- Interfacing with LISP
D.1 Interfacing protocols
So far, we have given a general picture of how the interpreter works, and an abstract description of the basic processes for finding, comparing, and modifying descriptions. In each case, we have listed the "central arguments" and explained how they are used, while pushing a whole bunch of other issues into the background. These other aspects must be dealt with in order to specify a real useable set of functions which integrate with INTERLISP control structure. There are several dimensions which are common to all of the functions, and these are discussed first:
* The basic CLISP form
* Process descriptions
* Specifying a context
The form of expressions for basic description operations
In KRL-0, there were LISP functions for Match, Seek, Describe, etc. Their arguments consisted of one or two fixed inputs (corresponding to the central arguments described for each process in section 1.2) and a signal table. The signal table had to handle all the other parameters concerning how they did their job and what kind of outputs they returned. The result was the expectable Turing tar-pit, in which the theoretical possibilities were unlimited, and the actual programs tended to stick to a tiny cluster of cases which made only trivial use of the flexibility.
In KRL-1 there is a much higher demand on the practical useability of these functions, since they are the only user-available means of accessing the description structures themselves. The programmer cannot resort to a concoction of (FindFiller(GetPerspective(GetDescriptors(FindSlot.....)))), but must be able to do all of the necessary work using the KRL-1 functions for the processes of finding, comparing and modifying data structures.
KRL-1 attempts to provide mechanisms which are easier to specify and control than the notion of signal table. The signal table (or its current equivalent -- see the section on discrimination nets) is still necessary to handle special cases and errors, but it is not the basic means of paramaterization. Instead we have analyzed the most important parameters of the different processes, and provided structured ways of specifying them. The majority of useful combinations can be specified directly in the syntactic form.
The basic problem which had to be faced in designing the syntax was the large number of variations allowed in what is to be specified. There are many different potential parameters, and in any one call only a few need be given. The rest either have default values, or are irrelevant because of choices made in the others.
This situation is closely analagous to the problem of specifying looping control structures in a language like LISP. There are a wide variety of means for producing the successive values of the iteration variable, such as stepping through a list (either tails or CARs); counting from some number to another (perhaps with an increment other than 1); pulsing a generator; calling a next-value function with the previous value as its argument; etc., etc. There are equally many ways to decide when to stop, and still more ways to decide what to return (the last value found, a list of results, a concatenation of results, etc.). In INTERLISP this problem has been dealt with by having an extremely general iterative statement based on labelled optional order-independent arguments, and a complex compiler which produces a piece of standard LISP code based on the iterative statement.
KRL-1 has adopted this style for the specification of basic processes. There is no LISP function named Match. Instead, Match is defined as a CLISP word (using mechanisms standardly available in INTERLISP), and there is a compiler which converts any form whose initial word is Match into a piece of LISP code expressing the computation. This code is invisible to the user, just as the translation of an iterative statement is invisible to the user. This compiler is integrated with the data structure compiler (see section?? <the compiling paper>), and in many of the standard cases produces highly efficient code in the form a a series of record accesses. Section 3.?3.??? discusses the nature of the code produced by the KRL-1 statement compiler.
The general form for a KRL-1 process statement (as it appears in LISP code) is:
(procedureName mainArg key1 arg1 key2 arg2.....)
For each procedure name (e.g. Match, Seek, Augment...) one of the central arguments is designated as the main argument, and follows the initial word. Other possible arguments are associated with key words (like the until, collect, etc. of the iterative statement) and can be included in any order, or omitted. Table ??? shows all of the allowable procedure names and keys. They will be explained one by one in the following sections which try to give an intuitive feel for the dimensions along which each parameter affects the reasoning process.
The compiler will take default values for omitted arguments, and will signal an error if there is insufficient information to carry out the process. The key names must appear explicitly in the form (i.e. they are not evaluated). All of the other arguments are assumed to be LISP variables or expressions, whose value is a KRL description (see discussion below on integrating LISP values into KRL expressions).
The decision to have all arguments be descriptions (rather than specially interpreted atoms, canned expressions, etc. as in KRL-0) makes the syntax more uniform, and leaves everything semantically extensible. This can be done without unacceptable loss of efficiency because of the ability to compile description manipulating expressions).
Theoretically, one could imagine specifying arbitrarily complex descriptions for any of the parameters. This flexibility exists, but we are directly implementing what appear to be the most useful cases. The compiler is written with these cases in mind, and is able to do far more towards producing efficient code for them, than for more general descriptions. For most parameters (see section ???) there is a set of pre-planned canned descriptions to be used. Specifying anything other than one of these will result in the interpreter falling back on user provided routines for handling the details (similar to the current user-responsibility for handling all of the possible signals in a signal table). We envision gradually expanding the library of useful parameter descriptions, and also providing better frameworks for users to write their own.
Process descriptions
In addition to specifying the inputs and the output form in a basic process statement, we need to be able to directly constrain and influence the course of the process which will be carried out. There are two major areas of specification:
* Limitations on inference and access
* Scheduling and resource information
These will be specified by the keywords allowing and scheduling respectively. Thus, we might see a call:
(Seek \The biter from a Bite with bitten = Sally/
returning \A PrimaryDescription/
allowing \A FullSearch with actionTable = !StandardTable37/
scheduling \A ScheduledProcess with resources = 20/)
Some of the syntax has not yet been explained. Sections ??? <krlsyntax.bravo> and ??? <defining the meaning of the different descriptor types> go into much more detail. For the moment, the following two facts are sufficient:
Within a character string intended for the LISP reader, the pair backslash/slash (\....../) delimits a section to be read by the KRL reader. The corresponding description appears in the LISP structure when it is done.
Within a KRL structure, the prefix character bang (!) indicates that the following single atom or S-expression is in LISP syntax. Its meaning is that the object being described is the value of the LISP expression at use time (not initial read time). For specifying a description to be added or sought, the sequence @! is used, for reasons which are semantically important, but are best understood later.