19.3 Reference Information

19.3.1 Loading Objects

Note: This file defines both macros and ordinary LISP functions. It must be loaded before any of these functions are used. The recommended way of doing this is to put the expression: (BothTimes (load objects)) at the beginning of your source file. This will cause the package to be loaded at both compile and load time.

19.3.2 Flavor Definition

Flavors are defined by using the macro defflavor This macro has the form:

(defflavor flavor-name (var1 var2 ...) (inheritance-flavor-list)
option1 option2 ...):list macro

flavor-name is the name of the flavor being defined. var1 var2 ... are the instance variables of the flavor. Each must be a symbol that is the name of the variable, or a list of two elements. The list form must have the name of the instance variable (id) as its first element and a default initialization form as its second argument. Do not use names like ”IF” or ”WHILE” for instance variables: they are translated freely within method bodies (see defmethod). The translation process is not very smart. For example, it doesn’t distinguish between formal parameters to methods with the same name as instance variables. For safety, if formal parameters are found that match instance variables, an error message is issued. Although the translation process isn’t smart, it at least understands the nature of quote.

The inheritance-flavor-list must be a list of one element or the empty list. When the empty list, it specifies that no inheritance is used in defining this flavor (for a description of inheritance, see section 10.3). When a one element list, the element must be the name of another flavor to inherit from. This other flavor must have already been compiled or loaded or an error message will be issued.

option1 option2 ... are the options that effect the instance variables and methods of the flavor. Recognized options are:

   (FAST-ACCESS-FOR-METHODS method1 method2 ...)

INITABLE-INSTANCE-VARIABLES [make all instance variables


GETTABLE-INSTANCE-VARIABLES [make all instance variables


SETTABLE-INSTANCE-VARIABLES [make all instance variables


FAST-ACCESS-FOR-METHODS [access all methods fast]

An empty list of variables is taken as meaning all variables rather than none, so (GETTABLE-INSTANCE-VARIABLES) is equivalent to GETTABLE-INSTANCE-VARIABLES.

Initable instance variables may be initialized using options to make-instance or instantiate-flavor. See below.

For each gettable instance variable a method of the same name is generated to access the instance variable. If instance variable LOCATION is gettable, one can invoke (=> <object> LOCATION).

For each settable instance variable a method with the name SET-<name> is generated. If instance variable LOCATION is settable, one can invoke (=> <object> SET-LOCATION <expression>). Settable instance variables are always also gettable and initable by implication. If this feature is not desired, define a method such as SET-LOCATION directly rather than declaring the instance variable to be settable.

FAST-ACCESS-FOR-METHODS is used to speed up message sending for a select set of methods. Methods specified in this option are accessed by a different means then ”normal” methods. This option should only be used when:

The order methods are specified with this option determines the order they are accessed (speed of method lookup). Thus, the first method specified will be accessed the quickest and the last method specified the slowest. If no methods are given (the second form above), the objects package will decide on its own order. Specifying no methods should be used only if a flavor has a few methods (i.e., less then 10). If any of the methods specified are not methods of the flavor, a warning message will be issued and fast access for it will be ignored.

As an example, consider the flavor TUNAFISH which has 70 methods. It is determined that much time is spent calling five low level TUNAFISH methods whose names are A, B, C, D, and E. Specifying:


in the flavor definition of TUNAFISH will cause method A to be accessed faster then any other method, B to be accessed slower then A, C slower then B, D slower then C, and finally E the slowest of the five methods. The remaining 65 methods of TUNAFISH will be accessed as slow or slower then E.

As its value, defflavor returns a list of the form:

         (flavor <flavor-name>)

Where <flavor-name> is the name of the flavor being defined. Examples of Flavor Definition

  (defflavor complex-number  
           (real-part imaginary-part)  
  (defflavor complex-number  
           ((real-part 0.0)  
            (imaginary-part 0.0)  
           (settable-instance-variables real-part)  

19.3.3 Method Definition

As you may recall, methods are defined using the macro defmethod. It has the form:

(defmethod (flavor-name method-name) ([arg1 arg2 ...])  
<expression1> <expression2>...): list                  macro

flavor-name is the name of the flavor a method is begin defined for. It must be an id.

method-name is the name of this method. It must also be an id.

arg1 arg2 ... are the arguments of the method. There may be zero or more and they must all be ids.

<expression1> <expression2>... make up the body of the method. This body can refer to any instance variable of the flavor by using the name just like an ordinary variable. They can also set them using setf. All occurrences of instance variables (except within evectors or quoted lists) are translated to an invocation of the form (EGETV SELF n).

The body of a method can also freely use SELF like it were another instance variable. SELF is bound to the object that the method applies to. SELF may not be setqed or setfed.

The value returned by defmethod is a list of the form:

         (method <flavor-name> <method-name>)

Where <flavor-name> and <method-name> are simply the names of the flavor being defined and method being defined respectively.

Examples of Method Definition:

  (defmethod (complex-number real-part) () real-part)  
  (defmethod (complex-number set-real-part) (new-real-part)  
  (setf real-part new-real-part))  
  (defmethod (toaster plug-into) (socket)  
  (setf plugged-into socket)  
  (=> socket assert-as-plugged-in self))

19.3.4 Object Creation

The most common way instances of a flavor are created is by using the function make-instance. It has the form:

(make-instance flavor-name:id [initialization-sequence]):
flavored object expr
make-instance takes as arguments a flavor name and an optional initialization sequence.

Flavor-name must be an id that represents an existing flavor.

The optional initialization-sequence, when specified, consists of alternating pairs of instance variable names and corresponding initial values. Note that the instance variable names are quoted, in the example above, because all the arguments are evaluated. make-instance returns an object that is ”initialized.”

Examples of make-instance

            (make-instance 'complex-number)  
            (make-instance 'complex-number 'real-part 0.0  
                                   'imaginary-part 1.0)

An object returned by make-instance is initialized as follows:

First, all instance variables with initialization specified in the call to make-instance are initialized to the value given. After this the objects DEFAULT-INIT method is envoked. This method is constructed by the flavor definition (defflavor). It initializes any instance variables not specified in the call to make-instance but having default initializations specified in the flavor definition. It then sets all remaining uninitialized instance variables to the value *UNBOUND*. The default initialization and initialization to *UNBOUND* is performed by the DEFAULT-INIT method.

If a method named INIT is defined for this flavor of object, it is invoked automatically after the initializations just discussed. The INIT method is passed, as its one argument, a list of alternating variable names and initial values. This list is the result of evaluating the initializations given to make-instance. For example, if we call:

(make-instance 'complex-number 'real-part (sin 30)  
                                'imaginary-part (cos 30))

then the argument to the INIT method (if any) would be

  (real-part .5 imaginary-part .866).

The INIT method may do anything desired to set up the desired initial state of the object.

At present, this value passed to the INIT method is of virtually no use to the INIT method since the values have been stored into the instance variables already. In the future, though, the objects package may be extended to permit keywords other than names of instance variables to be in the initialization part of calls to make-instance. If this is done, INIT methods will be able to use the information by scanning the argument.

For details on how initialization is done when inheritance is in effect, see Using Inheritance, section 10.3.

Another less common way to create an object is through the use of the function instantiate-flavor. It has the form:

(instantiate-flavor flavor name:any [U:list]):
flavored object expr
This is the same as make-instance, except that the initialization list is provided as a single, required argument. The list must be a list of alternating keyword names and initial values.


  (instantiate-flavor 'complex-number  
          (list  'real-part  (sin  30) 'imaginary-part (cos 30)))

19.3.5 Message Sending

There are several ways to invoke a method:

(=> object:object method:id): any expr
    (=> r real-part)

    (=> r set-real-part 1.0)

The message name is not quoted. Arguments to the method are supplied as arguments to =>. In these examples, r is the object, real-part and set-real-part are the methods, and 1.0 is the argument to the set-real-part method.

(send object:object method:id): any expr
    (send r 'real-part)

    (send r 'set-real-part 1.0)

The meanings of these two examples are the same as the meanings of the previous two. Only the syntax is different: the message name is quoted.

(send-if-handles object:object method:id [U:any]): any expr
Conditionally Send a Message


    (send-if-handles r 'real-part)

    (send-if-handles r 'set-real-part 1.0)

SEND-IF-HANDLES is like SEND, except that if the object defines no method to handle the message, no error is reported and NIL is returned.

(lexpr-send object:object method:id [U:any] V:list):any expr
Send a message (explicit ”Rest” Argument List)


    (lexpr-send foo 'bar a b c (list x y))

The last argument to LEXPR-SEND is a list of the remaining arguments.

(lexpr-send-if-handles object:any
method:id [U:any] V:list):any expr
This is the same as LEXPR-SEND, except that no error is reported if the object fails to handle the message.

(lexpr-send-1 object:object method:id V:list):any expr
Send a Message (Explicit Argument List)
    (lexpr-send-1 r 'real-part nil)

    (lexpr-send-1 r 'set-real-part (list 1.0))

Note that the message name is quoted and that the argument list is passed as a single argument to LEXPR-SEND-1.

(lexpr-send-1-if-handles object:any
method:any V:list): any expr
This is the same as LEXPR-SEND-1, except that no error is reported if the object fails to handle the message.

(ev-send object:any method:any V:list): any expr
EV-SEND is just like LEXPR-SEND-1, except that it is an EXPR instead of a MACRO. Its sole purpose is to be used as a run-time function object, for example, as a function argument to a function.

19.3.6 Printing Objects

Objects can print out according to their flavor. If a flavor has a channelprin method, that method will be used by prin1, prin2, etc. to print objects of that flavor.

(=> any CHANNELPRIN channel level prin1?): any method
A channelprin method must accept three arguments: a channel (currently a small integer); either nil or a number which will indicate the current level of nesting within lists, vectors, or other objects; and a boolean which will indicate whether prin1- or prin2-style printing is being done.

Ordinarily, an object should print in the form #<flavor info ... >. If the level of printing is not nil, printing of lists, vectors, objects, etc. within the channelprin method should be done with prinlevel bound to 1 less than its value at the time of entry to the method.

19.3.7 Useful Functions on Objects

(object-type U:any): id, NIL expr
The object-type function returns the type (an id) of the specified object, or nil, if the argument is not an object. At present this function cannot be guaranteed to distinguish between objects created by the objects package and other LISP entities, but the only possible confusion is with vectors or evectors.

(flavor-defined? flavor-name:id): t, nil expr
Returns t if flavor-name is a currently defined flavor. Returns nil otherwise.

(declare-flavor < flavorname >< var1 >< var2 >...):
undefined macro

(undeclare-flavor < var1 >< var2 >...): undefined macro
⋆⋆⋆ Read these warnings carefully! ⋆⋆⋆

This facility can reduce the overhead of invoking methods on particular variables, but it should be used sparingly. It is not well integrated with the rest of the language. At some point a proper declaration facility is expected and then it will be possible to make declarations about objects, integers, vectors, etc., all in a uniform and clean way.

The declare-flavor macro allows you to declare that a specific symbol is bound to an object of a specific flavor. This allows the flavors implementation to eliminate the run-time method lookup normally associated with sending a message to that variable, which can result in an appreciable improvement in execution speed. This feature is motivated solely by efficiency considerations and should be used ONLY where the performance improvement is critical.

Details: if you declare the variable X to be bound to an object of flavor FOO, then within the context of the declaration (see below), expressions of the form (=> X GORP ...) or (SEND X ’GORP ...) will be replaced by function invocations of the form (FOO GORP X ...). Note that there is no check made that the flavor FOO actually contains a method GORP.If it does not, then a run-time error ”Invocation of undefined function FOO”GORP” will be reported.

WARNING: The declare-flavor feature is not presently well integrated with the compiler. Currently, the declare-flavor macro may be used only as a top-level form, like the PSL FLUID declaration. It takes effect for all code evaluated or compiled henceforth. Thus, if you should later compile a different file in the same compiler, the declaration will still be in effect! this is a dangerous crock, so be careful! To avoid problems, I recommend that declare-flavor be used only for uniquely-named variables. The effect of a declare-flavor can be undone by an undeclare-flavor, which also may be used only as a top-level form. Therefore, it is good practice to bracket your code in the source file with a declare-flavor and a corresponding undeclare-flavor.

  ⋆⋆⋆ Did you read the above warnings??? ⋆⋆⋆

(object-get-handler object:object method-name:id): id expr
Note that the use of object-get-handler is not recommended. It should only be used in the most critical places where the time to perform a normal message send is too slow and the declare-flavor facility cannot be used.

Given an object and a method-name, object-get-handler returns the name of the function that implements this method. It is used to repetitively send the same message. For example:

           ((func-name (object-get-handler obj 'banana)))  
           (do (idapply func-name (list arg1 arg2 ... argn)))  

This is equivalent to sending the message:

         (=> obj banana arg1 arg2 ... argn)

inside the for but the lookup of the function name for the message send is only performed once.

Miscellaneous Functions

(objects-version): NIL expr
This function prints a date used to check the version of the objects package in use. When bugs are reported, the version date should be given. Older versions of the objects package did not have this function defined.