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.
Flavors are defined by using the macro defflavor This macro has the form:
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:
|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:
Where <flavor-name> is the name of the flavor being defined. Examples of Flavor Definition
As you may recall, methods are defined using the macro defmethod. It has the form:
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:
Where <flavor-name> and <method-name> are simply the names of the flavor being defined
and method being defined respectively.
Examples of Method Definition:
The most common way instances of a flavor are created is by using the function make-instance. It has the form:
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
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:
then the argument to the INIT method (if any) would be
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:
There are several ways to invoke a method:
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.
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 is like SEND, except that if the object defines no method to handle the message, no error is reported and NIL is returned.
The last argument to LEXPR-SEND is a list of the remaining arguments.
Note that the message name is quoted and that the argument list is passed as a single argument to LEXPR-SEND-1.
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.
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.
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.
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
This is equivalent to sending the message:
inside the for but the lookup of the function name for the message send is only performed once.