Users should note that in 1988, the ANSI X3J13 committee (which is developing a
standard for Common Lisp) adopted a new object oriented paradigm for ANSI Common Lisp.
This new paradigm is called CLOS. Allegro CL has included CLOS since release 4.0. This
chapter describes Flavors, an older object oriented paradigm developed in the early
1980's, which Allegro CL has supported as an extension since very early versions. While
there are no plans to remove support from Flavors, new applications should consider using
CLOS rather than Flavors since CLOS features more modern programming techniques and can be
expected to be more portable between different implementations of Common Lisp.

The object-oriented programming style used in the Smalltalk and Actor
families of languages is available in Allegro CL. Its purpose is to perform generic
operations on objects. Part of its implementation is simply a convention in
procedure-calling style; part is a powerful language feature, called Flavors, for defining
abstract objects. This chapter explains the principles of object-oriented programming and
message passing, and the use of Flavors in implementing these in Allegro CL. It assumes no
prior knowledge of any other languages.

The implementation of Flavors distributed with Allegro CL is proprietary code which
employs special interpreter and compiler hooks for very efficient execution. It is also
quite similar to that in Symbolics Lisp, although a few details and extensions differ.
Most code should port easily between the two.

Unless otherwise indicated, all the symbols defined in this chapter are exported from
the flavors package. Users must either use the qualifier flavors: or execute

(use-package :flavors)

before using flavors code.

Warning: two symbols in the flavors package have the same names as
symbols in the common-lisp package. These are defmethod and make-instance
(both part of CLOS as well as Flavors). Therefore, if a package needs to use both the flavors
and the common-lisp packages, you must shadow the symbols from one package.
Suppose you want the foo package to use both flavors and common-lisp
and have the unqualified symbols come from the flavors package. The following
two pieces of code achieve that end, the first assumes the foo package
already exists and the second creates it.

The flavors module is not loaded in the default initial Lisp executable, the flavors
package does not exist and no action triggers the autoloading of flavors.fasl. You must
load flavors by evaluating the following form:

(require :flavors)

You should put such a form at the start of any source file using flavors. There are no
individual description pages on flavors functionality. This document is the entire and
complete flavors documentation.

When writing a program, it is often convenient to model what the program does in terms
of objects, conceptual entities that can be likened to real-world things. Choosing
what objects to provide in a program is very important to the proper organization of the
program. In an object-oriented design, specifying what objects exist is the first task in
designing the system. In a text editor, the objects might be pieces of text, pointers
into text, and display windows. In an electrical design system, the objects
might be resistors, capacitors, transistors,wires, and display
windows. After specifying what objects there are, the next task of the design is to
figure out what operations can be performed on each object. In the text editor example,
operations on pieces of text might include inserting text and deleting text;
operations on pointers into text might include moving forward and backward; and
operations on display windows might include redisplaying the window and changing
which piece of text the window is associated with.

In this model, we think of the program as being built around a set of objects, each of
which has a set of operations that can be performed on it. More rigorously, the program
defines several types of objects (the editor above has three types), and it can
create many instances of each type (that is, there can be many pieces of text, many
pointers into text, and many windows). The program defines a set of types of objects and,
for each type, a set of operations that can be performed on any object of the type.

The new type abstractions may exist only in the programmer's mind. The mapping into a
concrete representation may be done without the aid of any programming features. For
example, it is possible to think of an atom's property list as an implementation of an
abstract data type on which certain operations are defined in terms of the Lisp get
function. There are other property lists which are not stored in the structure of a
symbol, defined in terms of the Common Lisp getf function. Such a property list is just a
list with an even number of items. This type can be instantiated with any function that
creates a list; for example, the form (list 'a 23) creates a new property list with a
single key/value pair. The fact that property lists are really implemented as lists,
indistinguishable from any other lists, does not invalidate this point of view. However,
such conceptual data types cannot be distinguished automatically by the system; one cannot
ask: is this object a disembodied property list, as opposed to an ordinary list?

Use of defstruct is another mechanism for creating new data types. This is reviewed in
the next section, where a data type for ship is used as an example. defstruct
automatically defines some operations on the objects: the operations to access its
elements. We could define other functions that did useful computation with ships, such as
computing their speed, angle of travel, momentum, or velocity, stopping them, moving them
elsewhere, and so on.

In both cases, we represent our conceptual object by one Lisp object. The Lisp object
we use for the representation has structure and refers to other Lisp objects. In
the case of a property list, the Lisp object is a list of pairs; in the ship case, the
Lisp object is an array or vector whose details are taken care of by defstruct. In both
cases, we can say that the object keeps track of an internal state, which can be examined
and altered by the operations available for that type of object. getf examines the
state of a property list, and setf of getf alters it; ship-x-position examines the state
of a ship, and

(setf (ship-x-position ship) 5.0)

alters it.

This is the essence of object-oriented programming. A conceptual object is modeled by a
single Lisp object, which bundles up some state information. For every type of object,
there is a set of operations that can be performed to examine or alter the state of the
object.

An important benefit of the object-oriented style is that it lends itself to a
particularly simple and clear kind of modularity. If you have modular programming
constructs and techniques available, they help and encourage you to write programs that
are easy to read and understand, and so are more reliable and maintainable.
Object-oriented programming lets a programmer implement a useful facility that presents
the caller with a set of external interfaces, without requiring the caller to understand
how the internal details of the implementation work. In other words, a program that calls
this facility can treat the facility as a black box; the calling program has an implicit
contract with the facility guaranteeing the external interfaces, and that is all it knows.

For example, a program that uses disembodied property lists never needs to know that
the property list is being maintained as a list of alternating indicators and values; the
program simply performs the operations, passing them inputs and getting back outputs. The
program depends only on the external definition of these operations: it knows that if it
stores a property by doing a setf of a getf, and doesn't remf it (or setf over it), then
it can use getf to be sure of getting back the same thing which was put in. This hiding of
the details of the implementation means that someone reading a program that uses
disembodied property lists need not concern himself with how they are implemented; he need
only understand what abstract operations are represented. This lets the programmer
concentrate his energies on building a higher-level program rather than understanding the
implementation of the support programs. This hiding of implementation means that the
representation of property lists could be changed and the higher-level program would
continue to work. For example, instead of a list of alternating elements, the property
list could be implemented as an association list or a hash table. Nothing in the calling
program would change at all.

The same is true of the ship example. The caller is presented with a collection of
operations, such as ship-x-position, ship-y-position, ship-speed, and ship-direction; it
simply calls these and looks at their answers, without caring how they did what they did.
In our example above, ship-x-position and ship-y-position would be accessor functions,
defined automatically by defstruct, while ship-speed and ship-direction would be functions
defined by the implementor of the ship type. The code might look like this:

The caller need not know that the first two functions were structure accessors and that
the second two were written by hand and perform arithmetic. Those facts would not be
considered part of the black-box characteristics of the implementation of the ship
type. The ship type does not guarantee which functions will be implemented in
which ways; such aspects are not part of the contract between ship and its
callers. In fact, ship could have been written this way instead:

In this second implementation of the ship type, we have decided to store the velocity
in polar coordinates instead of rectangular coordinates. This is purely an implementation
decision. The caller has no idea which of the two ways the implementation uses; he just
performs the operations on the object by calling the appropriate functions.

We have now created our own types of objects, whose implementations are hidden from the
programs that use them. Such types are usually referred to as abstract types. The
object-oriented style of programming can be used to create abstract types by hiding the
implementation of the operations and simply documenting what the operations are defined to
do.

Some more terminology: the quantities being held by the elements of the ship
structure are referred to as instance variables. Each instance of a type has the
same operations defined on it; what distinguishes one instance from another (besides
eqness) is the values that reside in its instance variables. The example above illustrates
that a caller of operations does not know what the instance variables are; our two ways of
writing the ship operations have different instance variables, but from the outside they
have exactly the same operations.

One might ask: but what if the caller evaluates (svref ship 2) and notices
that he gets back the x-velocity rather than the speed? Then he can tell which of the two
implementations were used. This is true; if the caller were to do that, he could tell.
However, when a facility is implemented in the object-oriented style, only certain
functions are documented and advertised, the functions that are considered to be
operations on the type of object. The contract from ship to its callers only
speaks about what happens if the caller calls these functions. The contract makes no
guarantees at all about what would happen if the caller were to start poking around on his
own using svref. A caller who does so is in error. He is depending on the concrete
implementation of the abstraction: something that is not specified in the contract. No
guarantees were ever made about the results of such action, and so anything may happen;
indeed, if ship were reimplemented, the code that does the svref might have a
different effect entirely and probably stop working. This example shows why the concept of
a contract between a callee and a caller is important: the contract specifies the
interface between the two modules.

Unlike some other languages that provide abstract types, Allegro CL makes no attempt to
have the language automatically forbid constructs that circumvent the contract. This is
intentional. One reason for this is that Lisp is an interactive system, and so it is
important to be able to examine and alter internal state interactively (usually from a
debugger). Furthermore, there is no strong distinction between the system and the user
portions of the Allegro CL system; users are allowed to get into nearly any part of the
language system and change what they want to change.

In summary: by defining a set of operations and making only a specific set of external
entry-points available to the caller, the programmer can create his own abstract types.
These types can be useful facilities for other programs and programmers. Since the
implementation of the type is hidden from the callers, modularity is maintained and the
implementation can be changed easily.

We have hidden the implementation of an abstract type by making its operations into
functions which the user may call. The importance of the concept is not that they are
functions -- in Lisp everything is done with functions. The important point is that we
have defined a new conceptual operation and given it a name, rather than requiring each
user who wants to do the operation to write it out step-by-step. Thus we say

(ship-x-velocity s)

rather than

(aref s 2)

Often a few abstract operation functions are simple enough that it is desirable to
compile special code for them rather than really calling the function. (Compiling special
code like this is often called open-coding.) The compiler is directed to do this
through use of macros for example. defstruct arranges for this kind of special compilation
for the functions that get the instance variables of a structure.

When we use this optimization, the implementation of the abstract type is only hidden
in a certain sense. It does not appear in the Lisp code written by the user, but does
appear in the compiled code. The reason is that there may be some compiled functions that
use the macros (or other concrete manifestation of the implementation). Even if you change
the definition of the macro, the existing compiled code will continue to use the old
definition. Thus, if the implementation of a module is changed, programs that use it may
need to be recompiled. This sacrifice of compatibility between interpreted and compiled
code is usually quite acceptable for the sake of efficiency in debugged code.

In the Allegro CL implementation of Flavors that is discussed below, there is never any
such incorporation of nonmodular knowledge into a program by either the interpreter or the
compiler, except when the :ordered-instance-variables feature is used (described below).
If you don't use the :ordered-instance-variables feature, you don't have to worry about
incompatibilities.

Consider the rest of the program that uses the ship abstraction. It may want to deal
with other objects that are like ships in that they are movable objects with mass, but
unlike ships in other ways. A more advanced model of a ship might include the concept of
the ship's engine power, the number of passengers on board, and its name. An object
representing a meteor probably would not have any of these, but might have another
attribute such as how much iron is in it.

However, all kinds of movable objects have positions, velocities, and masses, and the
system will contain some programs that deal with these quantities in a uniform way,
regardless of what kind of object is being modeled. For example, a piece of the system
that calculates every object's orbit in space need not worry about the other, more
peripheral attributes of various types of objects; it works the same way for all objects.
Unfortunately, a program that tries to calculate the orbit of a ship needs to know the
ship's attributes, and must therefore call ship-x-position and ship-y-velocity
and so on. The problem is that these functions won't work for meteors. There would have to
be a second program to calculate orbits for meteors that would be exactly the same, except
that where the first one calls ship-x-position, the second one would call
meteor-x-position, and so on. This would be very bad; a great deal of
code would have to exist in multiple copies, all of it would have to be maintained in
parallel, and it would take up space for no good reason.

What is needed is an operation that can be performed on objects of several different
types. For each type, it should do the thing appropriate for that type. Such operations
are called generic operations. The classic example of generic operations is the
arithmetic functions in many programming languages, including Allegro CL. The +
function accepts integers, floats or bignums and performs an appropriate kind of addition
based on the data types of the objects being manipulated. In MACSYMA, a large algebraic
manipulation system implemented in Lisp, the + operation works for
matrices, polynomials, rational functions, and arbitrary algebraic expression trees. In
our example, we need a generic x-position operation that can be performed
on either ships, meteors, or any other kind of mobile object represented in the system.
This way, we can write a single program to calculate orbits. When it wants to know the x
position of the object it is dealing with, it simply invokes the generic x-position
operation on the object, and whatever type of object it has, the correct operation is
performed, and the x position is returned.

In the following discussion we use another idiom adopted from the Smalltalk language:
performing a generic operation is called sending a message. The message consists of
an operation name (a symbol) and arguments. One can imagine objects in the program as
`little people' who accept messages and respond to them with answers (returned values). In
the example above, an object is sent an x-position message, to which it
responds with its x position.

Sending a message is a way of invoking a function without specifying which function is
to be called. Instead, the data determines the function to use. The caller specifies an
operation name and an object; that is, it said what operation to perform, and what object
to perform it on. The function to invoke is found from this information.

The two data used to figure out which function to call are the type of the
object, and the name of the operation. The same set of functions is used for all
instances of a given type, so the type is the only attribute of the object used to figure
out which function to call. The rest of the message besides the operation is data which
are passed as arguments to the function, so the operation is the only part of the message
used to find the function. Such a function is called a method. For example, if we
send an x-position message to an object of type ship, then
the function we find is the ship type's x-position method. A
method is a function that handles a specific operation on a specific kind of object; this
method handles messages named x-position to objects of type ship.

In our new terminology: the orbit-calculating program finds the x position of the
object it is working on by sending that object a message consisting of the operation x-position
and no arguments. The returned value of the message is the x position of the
object. If the object was of type ship, then the ship type's x-position
method was invoked; if it was of type meteor, then the meteor type's x-position
method was invoked. The orbit-calculating program just sends the message, and the right
function is invoked based on the type of the object. We now have true generic functions,
in the form of message passing: the same operation can mean different things depending on
the type of the object.

How do we implement message passing in Lisp? Our convention is that objects that
receive messages are always functional objects (that is, you can apply them to
arguments). A message is sent to an object by calling that object as a function, passing
the operation name as the first argument and the arguments of the message as the rest of
the arguments. Operation names are represented by symbols; normally these symbols are in
the keyword package, since messages may normally be passed between objects defined in
different packages. So if we have a variable my-ship whose value is an object of type
ship, and we want to know its x position, we send it a message as follows:

(send my-ship :x-position)

To set the ship's x position to 3.0, we send it a message like this:

(send my-ship :set-x-position 3.0)

A variation supported in some Flavor systems would allow

(send my-ship :set :x-position 3.0)
;;; not supported

but this is now deprecated and not provided in Allegro CL.

It should be stressed that no new features are added to Lisp for message sending; we
simply define a convention on the way objects take arguments. The convention says that an
object accepts messages by always interpreting its first argument as an operation name.
The object must consider this operation name, find the function which is the method for
that operation, and invoke that function.

To emphasize the relationship between well-known features and the new object-oriented
version, we define the two basic functions for message passing as follows:

This macro expands to an equivalent to funcall: anywhere send is used, funcall
could just as well appear. send is potentially more efficient because while funcall
must determine the type of its first argument at runtime, the first argument to send is
implicitly known to be a flavor instance. In any case, the function send is preferable to funcall
when a message is being sent, since it documents that Flavors and message sending are
being used.

Conceptually, this sends object a message with operation and arguments as specified.

In some implementations of Flavors, the semantics of send may differ
from funcall in those cases where object is a symbol, list, number, or other object that
does not normally handle messages.

This macro is equivalent to apply; see the notes above for send.
The last argument should be a list.

How does this all work? The object must somehow find the right method for the message
it is sent. Furthermore, the object now has to be callable as a function. However, an
ordinary function will not do: we need a data structure that can store the instance
variables (the internal state) of the object. Of the Allegro CL features available, the
most appropriate is the closure. A message-receiving object could be implemented as a
closure over a set of instance variables. The function inside the closure would have a big
case form to dispatch on its first argument.

While closures would work, they would have several problems. The main problem is that
in order to add a new operation to a system, it is necessary to modify code in more than
one place: you have to find all the types that understand that operation, and add a
new clause to the case. The problem with this is that you cannot textually separate the
implementation of your new operation from the rest of the system: the methods must be
interleaved with the other operations for the type. Adding a new operation should only
require adding Lisp code; it should not require modifying Lisp code.

For example, the conventional way of making generic operations for arithmetic on
various new mathematical objects is to have a procedure for each operation (+, *, etc.),
which has a big case for all the types; this means you have to modify
code in generic-plus, generic-times, ... to add a type.
This is inconvenient and error-prone.

The flavor mechanism is a streamlined, more convenient, and time-tested system
for creating message-receiving objects. With flavors, you can add a new method simply by
adding code, without modifying existing code. Furthermore, many common and useful things
are very easy to do with flavors. The rest of this chapter describes flavors.

A flavor, in its simplest form, is a definition of an abstract type. New flavors
are created with the defflavor special form, and methods of the flavor are created with
the defmethod special form. New instances of a flavor are created with the make-instance
function. This section explains simple uses of these forms.

For an example of a simple use of flavors, here is how the ship example
above would be implemented.

The code above creates a new flavor. The first subform of the defflavor
is ship, which is the name of the new flavor. Next is the list of instance
variables; they should be familiar by now. The next subform is something we will get to
later. The rest of the subforms are the body of the defflavor, and each
one specifies an option about this flavor. In our example, there is only one option,
namely :gettable-instance-variables. This means that for each instance
variable, a method should automatically be generated to return the value of that instance
variable. The name of the operation is a symbol with the same name as the instance
variable, but interned in the keyword package. Thus, methods are created to handle the
operations :x-position, :y-position, and so on.

Each of the two defmethod forms adds a method to the flavor. The first
one adds a handler to the flavor ship for the operation :speed. The second subform is the
lambda-list, and the rest is the body of the function that handles the :speed operation.
The body can refer to or set any instance variables of the flavor, just like variables
bound by a containing let. When any instance of the ship flavor is invoked
with a first argument of :direction, the body of the second defmethod
is evaluated in an environment in which the instance variables of ship refer to the
instance variables of this instance (the one to which the message was sent). So the
arguments passed to atan are the velocity components of this particular
ship. The result of atan becomes the value returned by the :direction
operation.

Now we have seen how to create a new abstract type: a new flavor. Every instance of
this flavor has the five instance variables named in the defflavor form,
and the seven methods we have seen (five that were automatically generated because of the :gettable-instance-variables
option, and two that we wrote ourselves). The way to create an instance of our new flavor
is with the make-instance function. Here is how it could be used:

(setq my-ship (make-instance 'ship))

This returns an object whose printed representation is something like #<ship
13731210>. (The details of the print form will vary; it is an object which cannot be
read back in from this default shorthand printed representation.) The argument to make-instance
is the name of the flavor to be instantiated. Additional arguments, not used here, are init
options, that is, commands to the flavor of which we are making an instance, selecting
optional features. This will be discussed more in a moment.

The flavor we have defined is quite useless as it stands since there is no way to set
any of its instance variables. We can fix this up easily by putting the :settable-instance-variables
option into the defflavor form. This option tells defflavor
to generate methods for operations :set-x-position, :set-y-position,
and so on. Each such method takes one argument and sets the corresponding instance
variable to that value.

Another option we can add to the defflavor is :initable-instance-variables,
(alternative spelling for compatibility is :inittable-instance-variables)
which allows us to initialize the values of the instance variables when an instance is
first created. :initable-instance-variables does not create any methods; instead it
specifies initialization keywords named :x-position, :y-position,
etc., that can be used as init-option arguments to make-instance
to initialize the corresponding instance variables. The list of init options is sometimes
called the init-plist because it is like a property list.

Finally, the :gettable-instance-variables option generates methods to
return instance variables. These messages are named by the keyword with the same name as
the instance variable.

All we have to do is evaluate this new defflavor, and the existing
flavor definition is updated and now includes the new methods and initialization options.
In fact, the instance we generated a while ago now accepts the new operations! We can set
the mass of the ship we created by evaluating:

(send my-ship :set-mass 3.0)

and the mass instance variable of my-ship is properly set to 3.0.

If you want to play around with flavors, it is useful to know that describe
of an instance tells you the flavor of the instance and the values of its instance
variables. If we were to evaluate

A flavor can also establish default initial values for instance variables. These
default values are used when a new instance is created if the values are not initialized
any other way. The syntax for specifying a default initial value is to replace the name of
the instance variable by a list, whose first element is the name and whose second is a
form to evaluate to produce the default initial value. For example:

The value of x-position was initialized explicitly, so the default was ignored. The
value of y-position was initialized from the default value, which was 0.0. The two
velocity instance variables were initialized from their default values, which came from
two global variables. The value of mass was not explicitly initialized and did not have a
default initialization, so it was left as nil. Some flavor implementations
set an uninitialized instance variable to unbound rather than nil.

There are many other options that can be used in defflavor, and the
init options can be used more flexibly than just to initialize instance variables; full
details are given later in this document. But even with the small set of features we have
seen so far, it is easy to write object-oriented programs.

Now we have a system for defining message-receiving objects so that we can have generic
operations. If we want to create a new type called meteor that would accept the same
generic operations as ship, we could simply write another defflavor and
two more defmethod's that looked just like those of ship,
and then meteors and ships would both accept the same operations. Objects of type ship
would have some more instance variables for holding attributes specific to ships and some
more methods for operations that are not generic, but are only defined for ships; the same
would be true of meteor.

However, this would be a a wasteful thing to do. The same code has to be repeated in
several places, and several instance variables have to be repeated. The code now needs to
be maintained in many places, which is always undesirable. The power of flavors (and the
name flavors) comes from the ability to mix several flavors and get a new flavor.
Since the functionality of ship and meteor partially overlap, we can take the
common functionality and move it into its own flavor, which might be called moving-object.
We would define moving-object the same way as we defined ship in the previous section.
Then, ship and meteor could be defined like this:

These defflavor forms use the second subform, for which we previously
used (). The second subform is a list of flavors to be combined to form the new flavor;
such flavors are called components. Concentrating on ship for a moment (analogous
statements are true of meteor), we see that it has exactly one component flavor:
moving-object. It also has a list of instance variables, which includes only the
ship-specific instance variables and not the ones that it shares with meteor. By
incorporating moving-object, the ship flavor acquires all of its instance variables, and
so need not name them again. It also acquires all of moving-object's methods, too. So with
the new definition, ship instances still implement the :x-velocity and :speed operations
with the same meaning as before. However, the :engine-power operation is also understood
(and returns the value of the engine-power instance variable).

What we have done here is to take an abstract type, moving-object, and build two more
specialized and powerful abstract types on top of it. Any ship or meteor can do anything a
moving object can do, and each also has its own specific abilities. This kind of building
can continue; we could define a flavor called ship-with-passenger that was built on top of
ship, and it would inherit all of moving-object's instance variables and methods as well
as ship's instance variables and methods. Furthermore, the second subform of defflavor
can be a list of several components, meaning that the new flavor should combine all the
instance variables and methods of all the flavors in the list, as well as the ones those
flavors are built on, and so on. All the components taken together form a big tree of
flavors. A flavor is built from its components, its components' components, and so on. We
sometimes use the term components to mean the immediate components (the ones listed
in the defflavor), and sometimes to mean all the components (including
the components of the immediate components and so on). (Actually, it is not strictly a
tree, since some flavors might be components through more than one path. It is really a
directed graph; it can even be cyclic.)

The order in which the components are combined to form a flavor is important. The tree
of flavors is turned into an ordered list by performing a top-down, depth-first
walk of the tree, including non-terminal nodes before the subtrees they head,
ignoring any flavor that has been encountered previously somewhere else in the tree. For
example, if flavor-1's immediate components are flavor-2 and flavor-3, and flavor-2's
components are flavor-4 and flavor-5, and flavor-3's component was flavor-4, then the
complete list of components of flavor-1 would be:

(flavor-1, flavor-2, flavor-4, flavor-5, flavor-3)

The flavors earlier in this list are the more specific, less basic ones; in our
example, ship-with-passengers would be first in the list, followed by ship, followed by
moving-object. A flavor is always the first in the list of its own components. Notice that
flavor-4 does not appear twice in this list. Only the first occurrence of a flavor
appears; duplicates are removed. (The elimination of duplicates is done during the walk; a
cycle in the directed graph does not cause a non-terminating computation.)

The set of instance variables for the new flavor is the union of all the sets of
instance variables in all the component flavors. If both flavor-2 and flavor-3 have
instance variables named foo, then flavor-1 has an instance variable named foo, and all
methods that refer to foo refer to this same instance variable. Thus different components
of a flavor can communicate with one another using shared instance variables. (Often, only
one component ever sets the variable; the others only look at it.) The default initial
value for an instance variable comes from the first component flavor to specify one.

The way the methods of the components are combined is the heart of the flavor system.
When a flavor is defined, a single function, called a combined method, is
constructed for each operation supported by the flavor. This function is constructed out
of all the methods for that operation from all the components of the flavor. There are
many different ways that methods can be combined; these can be selected by the user when a
flavor is defined. The user can also create new forms of combination.

There are several kinds of methods, but so far, the only kinds of methods we have seen
are primary methods. The default way primary methods are combined is that all but
the earliest one provided are ignored. In other words, the combined method is simply the
primary method of the first flavor to provide a primary method. What this means is that if
you are starting with a flavor foo and building a flavor bar on top of it, then you can
override foo's method for an operation by providing your own method. Your method will be
called, and foo's will never be called.

Simple overriding is often useful; for example, if you want to make a new flavor bar
that is just like foo except that it reacts completely differently to a few operations.
However, often you don't want to completely override the base flavor's (foo's) method;
sometimes you want to add some extra things to be done. This is where combination of
methods is used.

The usual way methods are combined is that one flavor provides a primary method, and
other flavors provide daemon methods. The idea is that the primary method is in
charge of the main business of handling the operation, but other flavors just want to
keep informed that the message was sent, or just want to do the part of the operation
associated with their own area of responsibility.

Daemon methods come in two kinds, before and after. There is a special
syntax in defmethod for defining such methods. For example, the following code defines an
after-daemon method for the :set-mass operation of the ship flavor:

Now when a message is sent, it is handled by a new function called the combined
method. The combined method first calls all of the before daemons, then the primary
method, then all the after daemons. Each method is passed the same arguments that the
combined method was given. The returned values from the combined method are the values
returned by the primary method; any values returned from the daemons are ignored.
Before-daemons are called in the order that flavors are combined, while after-daemons are
called in the reverse order. In other words, if you build bar on top of foo,
then bar's before-daemons run before any of those in foo, and bar's after-daemons run
after any of those in foo.

The reason for this order is to keep the modularity order correct. If we create
flavor-1 built on flavor-2, then the components of flavor-2 should not matter. Our new
before-daemons go before all methods of flavor-2, and our new after-daemons go after all
methods of flavor-2. Note that if you have no daemons, this reduces to the form of
combination described above. The most recently added component flavor is the highest level
of abstraction; you build a higher-level object on top of a lower-level object by adding
new components to the front. The syntax for defining daemon methods can be found in the
description of defmethod below.

To make this a bit more clear, let's consider a simple example that is easy to play
with: the :print-self method. The Lisp printer (i.e. the print function) prints instances
of flavors by sending them :print-self messages. The first argument to the :print-self
operation is a stream (we can ignore the others for now), and the receiver of the message
is supposed to print its printed representation to that stream. In the ship example above,
the reason that instances of the ship flavor printed the way they did is because the ship
flavor was actually built on top of a very basic flavor called vanilla-flavor; this
component is provided automatically by defflavor. It was vanilla-flavor's
:print-self method that was doing the printing. Now, if we give ship its own
primary method for the :print-self operation, then that method completely takes over the
job of printing: vanilla-flavor's method will not be called at all. However, if we give
ship a before-daemon method for the :print-self operation, then it will get invoked before
the vanilla-flavor method, and so whatever it prints will appear before what
vanilla-flavor prints. So we can use before-daemons to add prefixes to a printed
representation; similarly, after-daemons can add suffixes.

There are other ways to combine methods besides daemons, but this way is the most
common. The more advanced ways of combining methods are explained in a later section. The
details of vanilla-flavor and what it does for you are also explained later.

The vars list names of the instance-variables which contain the local state of
a flavor instance. Each element on this list is either a symbol naming the instance
variable or a two-element list of the symbol and a default initialization form. The
initialization form is evaluated when an instance of the flavor is created if no other
initial value for the variable is obtained. If no initialization is specified, the
variable has value nil.

The flavors are the names of the component flavors out of which this flavor is
built. The features of those flavors are inherited as described previously.

Each of the options may be either a keyword symbol or a list of a keyword symbol and
arguments. The options to defflavor are described in 8.0 Defflavor options, below.

Once a flavor is defined via defflavor the flavor name becomes an
extension of the Common Lisp type system. type-of applied to an instance of that flavor
will return its flavor name. The form

(typep instance flavor-name)

returns t if the instance is of the named flavor, or of any flavor which
contains the named flavor as a component.

In Allegro CL objects which are instances of flavors are implemented by a hidden
internal data type, actually a kind of vector. The svref function can
access the slots of an instance. The zeroth slot points to the internal descriptor for
that flavor; successive slots hold the instance variables.

flavor-name is a symbol which is the name of the flavor which is to receive
the method. operation is a keyword symbol which names the operation to be
handled. method-type is a keyword symbol for the type of method; it is omitted
when you are defining a primary method. For some method-types, additional information is
expected. It comes after operation.

defmethod defines a method, that is, a function to handle a particular
operation for instances of a particular flavor. The meaning of method-type depends on what
style of method combination is declared for this operation. For instance, if :daemon
combination (the default style) is in use, method types :before and :after are allowed.
See section 11.0 Method Combination for a complete
description of the way methods are combined.

lambda-list describes the arguments and &aux variables of the function.
The first argument to the method, which is the operation name itself, is automatically
handled and so is not included in lambda-list. Note that all arguments to a method are
evaluated; that is, methods must be functions, not macros or special forms. The forms
are the function body; the value of the last form is returned when the method is applied.
Some methods can return multiple values, depending on the style of method combination
used.

If you redefine a method that is already defined, the new definition replaces the old
one. Given a flavor, an operation name, and a method type, there can only be one function
(with the exception of :case methods), so if you defmethod
a :before daemon method for the foo flavor to handle the :bar
operation, then you replace any previous before-daemon; however, you do not affect the
primary method or methods of any other type, operation or flavor.

Among other things, defmethod causes a function to be defun'ed. This
function can be identified with a function spec in one of these forms:

Such identification is particularly useful for tracing methods. Remember that since
trace normally interprets a list as a function name plus tracing options, it is necessary
to use a form similar to the following:

Returns an instance of the specified flavor which has just been created.

Arguments after the first are alternating init-option keywords and arguments
to those keywords. These options are used to initialize instance variables and to select
arbitrary options, as described above. An :init message is sent to the newly-created
object with one argument, the init-plist. This is a property-list containing the
init-option's specified and those defaulted from the flavor's :default-init-plist.
However, init keywords that simply initialize instance variables, and the corresponding
values, may be absent when the :init methods are called. make-instance is
an easy-to-call interface to instantiate-flavor, below.

This is an extended version of make-instance, giving you more
features. Note that it takes the init-plist as a single argument, rather than
taking a &rest argument of init options and values. This property list may be modified
during instance creation; properties from the default init-plist are added if they are not
already present, and some :init methods may do explicit (setf (getf ...)) onto the
init-plist. If the init-plist were contained as a literal constant in the calling code,
this would be an attempt to permanently modify the calling code. (This is illegal, but
might not actually signal an error.) Therefore the caller should make sure that the
init-plist is freshly recreated (e.g. with append) for each call to instantiate-flavor.

Because the instantiate-flavor preserves backward compatibility with
ZetaLisp, the init-plist has the form of a ZetaLisp disembodied property list; that
is, an odd-numbered list of which the first element is ignored. (A more precise definition
of a disembodied property list is a cons with a Common Lisp property list stored on its
cdr.) For this reason, instantiate-flavor is now somewhat deprecated; new code should use
make-instance instead.

Here is the sequence of actions by which instantiate-flavor creates a new instance:

The specified flavor's instantiation flavor function, if it exists, is called to
determine which flavor should actually be instantiated. If there is no instantiation
flavor function, the specified flavor is instantiated.

If the flavor's method hash-table and other internal information have not been computed
or are not up to date, they are computed. This process is known as flavor combination.
(It is also called, somewhat confusingly, flavor compilation but methods functions
can be compiled independently of flavor combination.) This may take a substantial amount
of time, but it happens only once for each time you define or redefine a particular
flavor.

The instance itself is created. The area argument is ignored by Allegro CL and refers to
consing in specified areas, a feature used in some Lisp machines.

Initial values of the instance variables are computed. If an instance variable is
declared initable, and a keyword with the same spelling as its name appears in init-plist,
the property for that keyword is used as the initial value.

Otherwise, if the default init-plist specifies such a property, the value form is
evaluated and the result used. Or, if the flavor definition specifies a default
initialization form, it is evaluated and that result is used. In either case, the
initialization may not refer to any instance variables, nor will the variable self be
bound to the new instance when they are evaluated. The value forms are evaluated before
the instance is actually allocated.

If an instance variable does not get initialized either of these ways it is left nil;
an :init method may initialize it (see below).

All remaining keywords and values specified in the :default-init-plist option to defflavor,
that do not initialize instance variables and are not overridden by anything explicitly
specified in init-plist are then merged into init-plist using setf of getf. The default
init plist of the instantiated flavor is considered first, followed by those of all the
component flavors in the standard order.

Keywords appearing in the init-plist but not defined with the :init-keywords option or
the :initable-instance-variables option for some component flavor are collected. If the
:allow-other-keys option is specified with a non-nil value (either in the
original init-plist argument or by some default init plist) then these unhandled
keywords are ignored. If the return-unhandled-keywords argument is non-nil, a
list of these keywords is returned as the second value of instantiate-flavor. Otherwise,
an error is signaled if any unrecognized init keywords are present.

For make-instance, or for instantiate-flavor when the send-init-message-p argument is t,
an :init message is sent to the newly-created instance. It is passed one argument which is
the init-plist. getf can be used to extract options from this property-list. Each flavor
that needs initialization can contribute an :init method by defining a daemon.

The :init methods should not look on the init-plist for keywords that simply initialize
instance variables (that is, keywords defined with :initable-instance-variables rather
than :init-keywords). The corresponding instance variables are already set up when the
:init methods are called, and sometimes the keywords and their values may actually be
missing from the init-plist if it is more efficient not to put them on. To avoid problems,
always refer to the instance variables themselves rather than looking for the init
keywords that initialize them.

This operation is implemented on all flavor instances. This message examines the
init keywords and perform whatever initializations are appropriate. init-plist is the
argument that was given to instantiate-flavor, and may be passed directly to getf to
examine the value of any particular init option.

A default method which does nothing is provided by si:vanilla-flavor. However, many
flavors add :before and :after daemons to it.

Undefines flavor flavor. All methods of the flavor are lost. flavor and all flavors
that depend on it are no longer valid to instantiate. If instances of the discarded
definition exist, they continue to use that definition.

When a message is sent to an object, the variable self is automatically bound to that
object for the benefit of methods which want to manipulate the object itself (as opposed
to its instance variables). self is a lexical variable, that is, its scope is
local to the method body.

Updates the internal data of the flavor and any flavors that depend on it. If single-op
is supplied non-nil, only the methods for that operation are changed. The
system does this when you define a new method that did not previously exist. If
use-old-combined-methods is t, then the existing combined method functions are used if
possible. New ones are generated only if the set of methods to be called has changed. If
use-old-combined-methods is nil, automatically-generated functions to call
multiple methods or to contain code generated by wrappers are regenerated unconditionally.
The default value of use-old-combined-methods is t. If do-dependents is nil,
only the specific flavor you specified is recompiled. Normally all flavors that depend on
it are also recompiled, i.e. the default value of do-dependents is t.

recompile-flavor affects only flavors that have already been combined.
Typically this means it affects flavors that have been instantiated, but does not bother
with mixins.

placed in a file to be compiled, directs the compiler to perform flavor
combination for the named flavors, forcing the generation and compilation of
automatically-generated combined methods at compile time. Furthermore, the internal data
structures needed to instantiate the flavor will be computed at load time, rather than
waiting for the first attempt to instantiate the flavor.

You should only use compile-flavor-methods on a flavor that is going
to be instantiated. For a flavor that is never going to be instantiated (that is, a flavor
that only serves to be a component of other flavors that actually do get instantiated), it
is a complete waste of time, except in the unusual case where those other flavors can
inherit the combined methods of this flavor instead of each one having its own copy of the
combined method which happens to be identical to the others. In this unusual case, you
should use the :abstract-flavor option to defflavor.

compile-flavor-methods forms should be compiled after all of the other
information needed to create the combined methods is available. You should put them after
all the definitions of all relevant flavors, wrappers, and methods of all components of
the argument flavors.

When a compile-flavor-methods form is seen by the interpreter, the
internal data structures are generated and the combined methods are defined and compiled.

Given an object and an operation, this returns the object's method for that
operation, or nil if it has none. When object is an instance of a flavor,
this function can be useful to find which of that flavor's components supplies the method.

This is equivalent to the :get-handler-for message provided by si:vanilla-flavor.

This function returns non-nil if the flavor named flavor-name allows
keyword in the init options when it is instantiated, or nil if it does not.
The non-nil value is the name of the component flavor that contributes the
support of that keyword. (Note that it is in the system package, which has the nickname
si.)

This function returns the value of the instance variable symbol inside instance. If
there is no such instance variable, an error is signaled, unless no-error-p is non-nil,
in which case nil is returned.

[Function]set-in-instance

Arguments:instance symbol value

This function sets the value of the instance variable symbol inside instance to value.
If there is no such instance variable, an error is signaled.

This function prints descriptive information about a flavor; it is
self-explanatory. An important thing it tells you that can be hard to figure out yourself
is the combined ordered list of component flavors; this list is what is printed after the
phrase `and directly or indirectly depends on.'

There are quite a few options to defflavor. They are all described
here, although some are for very specialized purposes and not of interest to most users.
Some options take additional arguments, and these are listed and described with the
option.

Several of these options declare things about instance variables. These options can be
given with arguments which are instance variables, or without any arguments in which case
they refer to all of the instance variables listed at the top of the defflavor.
This is not necessarily all the instance variables of the combined flavor, just the
ones mentioned in this flavor's defflavor. When instance-variable
arguments are given, they must be instance variables that were listed at the top of the defflavor;
otherwise they are assumed to be misspelled and an error is signaled. It is legal to
declare things about instance variables inherited from a component flavor, but to do so
you must list these instance variables explicitly in the instance variable list at the top
of the defflavor, or mention them in a required-instance-variable option.

Enables automatic generation of methods for getting the values of instance variables.
The operation name is the name of the variable, in the keyword package (i.e. it has a
colon in front of it).

Note that there is nothing special about these methods; you could easily define them
yourself. This option generates them automatically to save you the trouble of writing out
a lot of very simple method definitions. (The same is true of methods defined by the
:settable-instance-variables option.) If you define a method for the same operation name
as one of the automatically generated methods, the explicit definition replaces the
automatic one.

Causes automatic generation of methods for setting the values of instance variables.
The operation name is :set- followed by the name of the variable. All settable instance
variables are also automatically made gettable and initable. (See the note in the
description of the :gettable-instance-variables option, above.)

The instance variables listed as arguments, or all instance variables listed in this defflavor
if the keyword is given alone, are made initable. This means that they can be
initialized through use of a keyword (a colon followed by the name of the variable) as an
init-option argument to make-instance. For compatibility with certain other
implementations, the spelling :inittable-instance-variables is also accepted.

NOTE: Special instance variables are not implemented in Allegro CL. Instance variables
are scoped lexically inside a method in both compiled and interpreted code. Special
instance variables are unimplementable in Common Lisp for the same reasons that it is
impossible to close over a normal special variable. In any case, they interfere with
proper code modularity; the original designers of Flavors now deprecate them as a
misfeature except for very obscure (or historical) purposes. The Allegro CL implementation
ignores the :special-instance-variable specification other than issuing a warning message,
but the resulting code will be unlikely to do the right thing if the instance variables
were declared special for some particular purpose.

The arguments are declared to be valid keywords to use in instantiate-flavor when
creating an instance of this flavor (or any flavor containing it). The system uses this
for error-checking: before the system sends the :init message, it makes sure that all the
keywords in the init-plist are either initable instance variables or elements of this
list. If any are not recognized, an error is signaled. When you write an :init method that
accepts some keywords, they should be listed in the :init-keywords option of the flavor.
They will be inherited by any flavor that mixes in this one. If :allow-other-keys is used
as an init keyword with a non-nil value, this error check is suppressed, and
unrecognized keywords are simply ignored.

The arguments are alternating keywords and value forms, like a property list. When the
flavor is instantiated, these properties and values are put into the init-plist unless
already present. This allows one component flavor to default an option to another
component flavor. The value forms are only evaluated when and if they are used. For
example,

(:default-init-plist :frob-array

(make-array 100))

would provide a default frob array for any instance for which the user
did not provide one explicitly. The following specification prevents errors for unhandled
init keywords in all instantiations of this flavor and other flavors that depend on it.

Declares that any flavor incorporating this one that is instantiated into an object
must contain the specified instance variables. An error occurs if there is an attempt to
instantiate a flavor that incorporates this one if it does not have these in its set of
instance variables. Note that this option does not check the spelling of its arguments in
the way described at the start of this section. If it did, it would be useless.

Required instance variables may be freely accessed by methods just like normal instance
variables. The difference between listing instance variables here and listing them at the
front of the defflavor is that the latter declares that this flavor owns
those variables and accepts responsibility for initializing them, while the former
declares that this flavor depends on those variables but that some other flavor must be
provided to manage them and whatever features they imply.

It is important to note that a method cannot in general access instance variables that
are not declared in the instance variable list of the method's flavor's defflavor,
or in a :required-instance-variable clause. Thus this option serves the purpose of
declaration as well as error checking. Any attempt to refer to an undeclared instance
variable will actually be treated as a free reference to a special instance variable, and
the compiler will issue its usual warning. However, for compatibility with some other
implementations, at defflavor time the flavor system will traverse
whatever part of the component flavor tree is already defined to infer additional instance
variables. Although compatibility is worthwhile, this is something of a misfeature because
variable declarations can be inherited from distant and nonapparent sources. Thus, it is
possible for the functionality of code to change suddenly if, for instance, the order of
loading files changes. It is a good idea always to use explicit
:required-instance-variables clauses.

The arguments are names of operations that any flavor incorporating this one must
handle. An error occurs if there is an attempt to instantiate such a flavor and it is
lacking a method for one of these operations. Typically this option appears in the defflavor
for a base flavor. Usually this is used when a base flavor does a

(send self ...)

to send itself a message that is not handled by the base flavor itself; the idea
is that the base flavor will not be instantiated alone, but only with other components
(mixins) that do handle the message. This keyword allows the error of having no handler
for the message to be detected when the flavor is instantiated or when
compile-flavor-methods is done, rather than when the missing operation is used.

The arguments are names of flavors that any flavor incorporating this one must include
as components, directly or indirectly. The difference between declaring flavors as
required and listing them directly as components at the top of the defflavor
is that declaring flavors to be required does not make any commitments about where those
flavors will appear in the ordered list of components; that is left up to whoever does
specify them as components. Declaring a flavor to be required only provides error
checking: an attempt to instantiate a flavor that does not include the required flavors as
components signals an error. Compare this with :required-methods and
:required-instance-variables.

For an example of the use of required flavors, consider the ship example
given earlier, and suppose we want to define a relativity-mixin which increases the mass
dependent on the speed. We might write,

but this would lose because any flavor that had relativity-mixin as a component would
get moving-object right after it in its component list. As a base flavor,
moving-object should be last in the list of components so that other components mixed in
can replace its methods and so that daemon methods combine in the right order.
relativity-mixin has no business changing the order in which flavors are combined, which
should be under the control of its including flavor. For example,

(defflavor starship () (relativity-mixin
long-distance-mixin
ship))

should put moving-object last (inheriting it from ship). So instead of the definition
above we write,

(defflavor relativity-mixin ()
()
(:required-flavors moving-object))

which allows relativity-mixin's methods to access moving-object's instance
variables such as mass, but does not specify any place for moving-object in
the list of components. (This assumes moving-object is already defined. See
the comment under :required-instance-variables.)

It is very common to specify the base flavor of a mixin with the
:required-flavors option in this way.

The arguments are names of flavors to be included in this flavor. The difference
between declaring flavors here and declaring them at the top of the defflavor
is that when component flavors are combined, if an included flavor is not specified as a
normal component, it is inserted into the list of components immediately after the last
component to include it. Thus included flavors act like defaults. The important thing is
that if an included flavor is specified as a component, its position in the list of
components is completely controlled by that specification, independently of where the
flavor appears as an :included-flavor.

:included-flavors and :required-flavors are used in similar ways; it would have been
reasonable to use :included-flavors in the relativity-mixin example above. The difference
is that when a flavor is required but not given as a normal component, an error is
signaled, but when a flavor is included but not given as a normal component, it is
automatically inserted into the list of components at a reasonable place.

Normally when a flavor is instantiated, the special flavor si:vanilla-flavor is
included automatically at the end of its list of components. The vanilla flavor provides
some default methods for the standard operations which all objects are supposed to
understand. These include :print-self, :describe, :which-operations, and several other
operations.

If any component of a flavor specifies the :no-vanilla-flavor option, then
si:vanilla-flavor is not included in that flavor. This option should not be used casually.

The argument is the name of a function that is to be called to handle any operation for
which there is no method. When an instance is sent a message for which it has no handler,
the flavor system uses the handler for the :unclaimed-message message, if there is one;
otherwise it uses the function specified in this option. Its arguments are the arguments
of the send which invoked the operation, including the instance itself as the first
argument. Usually, default handlers should be permissive about the number of arguments.
Whatever values the default handler returns are the values of the operation.

Default handlers can be inherited from component flavors. If a flavor has no other
default handler, one is provided which signals an error.

This option is mostly for esoteric internal system uses. The arguments are names of
instance variables which must appear first (and in this order) in all instances of this
flavor, or any flavor depending on this flavor. This is used for instance variables that
are specially known about by other code (e.g. non-Lisp) and also in connection with the
:outside-accessible-instance-variables option. If the keyword is given alone, the
arguments default to the list of instance variables given at the top of this defflavor.

Any number of flavors to be combined together can specify this option. The longest
ordered variable list applies, and an error is signaled if any of the other lists do not
match its initial elements.

Removing any of the :ordered-instance-variables, or changing their positions in the
list, requires that you recompile all methods that use any of the affected instance
variables.

The arguments are instance variables which are to be accessible from outside of this
flavor's methods. A macro is defined which takes an object of this flavor as an argument
and returns the value of the instance variable; setf may be used to set the value of the
instance variable. The name of the macro is the name of the flavor concatenated with a
hyphen and the name of the instance variable. These macros are similar to the accessors
created by defstruct.

This feature works in two different ways, depending on whether or not the instance
variable has been declared to have a fixed slot in all instances, via the
:ordered-instance-variables option.

If the variable is not ordered, the position of its value cell in the instance must be
computed at run time. This takes noticeable time, possibly more or less than actually
sending a message would take. An error is signaled if the argument to the accessor macro
is not an instance or is an instance that does not have an instance variable with the
appropriate name. However, there is no error check that the flavor of the instance is the
flavor the accessor macro was defined for, or a flavor built upon that flavor. This error
check would be too expensive.

If the variable is ordered, the compiler compiles a call to the accessor macro into a
primitive (actually a svref) which simply accesses that variable's assigned slot by
number. No error-checking is performed to make sure that the argument is really an
instance, much less that it is of the appropriate type.

Normally the accessor macro created by the :outside-accessible-instance-variables
option to access the flavor f's instance variable v is named f-v.
This option allows something other than the flavor name to be used for the first part of
the macro name. Specifying (:accessor-prefix get$) causes it to be named get$v
instead.

Marks this flavor as being an alias for another flavor. This flavor should have only
one component, which is the flavor it is an alias for, and no instance variables or other
options. No methods should be defined for it.

The effect of the :alias-flavor option is that an attempt to instantiate this flavor
actually produces an instance of the other flavor. Without this option, it would make an
instance of this flavor, which might behave identically to an instance of the other
flavor. :alias-flavor eliminates the need for separate mapping tables, method tables, etc.
for this flavor, which becomes truly just another name for its component flavor.

The alias flavor and its base flavor are also equivalent when used as an argument of
subtypep or as the second argument of typep; however, if the alias status of a flavor is
changed, you must recompile any code which uses it as the second argument to typep in
order for such code to function.

:alias-flavor is mainly useful for changing a flavor's name gracefully.

This option marks the flavor as one that is not supposed to be instantiated (that is,
is supposed to be used only as a component of other flavors). An attempt to instantiate
the flavor signals an error.

It is sometimes useful to do compile-flavor-methods on a flavor that is not going to be
instantiated, if the combined methods for this flavor will be inherited and shared by many
others. :abstract-flavor tells compile-flavor-methods not to complain about missing
required flavors, methods or instance variables. Presumably the flavors that depend on
this one and actually are instantiated will supply what is lacking.

Specifies the method combination style to be used for certain operations. Each argument
to this option is a list

(style order operation1 operation2 ...)

operation1, operation2, etc. are names of operations whose methods are to be
combined in the declared fashion. style is a keyword that specifies a style of
combination. order is a keyword whose interpretation is up to style; typically it is
either :base-flavor-first or :base-flavor-last.

Any component of a flavor may specify the type of method combination to be used for a
particular operation. If no component specifies a style of method combination, then the
default style is used, namely :daemon. If more than one component of a flavor specifies
the combination style for a given operation, then they must agree on the specification, or
else an error is signaled.

The following organization conventions are recommended for programs that use flavors.

A base flavor is a flavor that defines a whole family of related flavors, all of
which have that base flavor as a component. Typically the base flavor includes things
relevant to the whole family, such as instance variables, :required-methods and
:required-instance-variables declarations, default methods for certain operations,
:method-combination declarations, and documentation on the general protocols and
conventions of the family. Some base flavors are complete and can be instantiated, but
most cannot be instantiated themselves. They serve as a base upon which to build other
flavors. The base flavor for the foo family is often named basic-foo.

A mixin flavor is a flavor that defines one particular feature of an object. A
mixin cannot be instantiated, because it is not a complete description. Each module or
feature of a program is defined as a separate mixin; a usable flavor can be constructed by
choosing the mixins for the desired characteristics and combining them, along with the
appropriate base flavor. By organizing your flavors this way, you keep separate features
in separate flavors, and you can pick and choose among them. Sometimes the order of
combining mixins does not matter, but often it does, because the order of flavor
combination controls the order in which daemons are invoked and wrappers are wrapped. Such
order dependencies should be documented as part of the conventions of the appropriate
family of flavors. A mixin flavor that provides the mumble feature is often named
mumble-mixin.

If you are writing a program that uses someone else's facility, using that facility's
flavors and methods, your program may still define its own flavors, in a simple way. The
facility provides a base flavor and a set of mixins: the caller can combine these in
various ways depending on exactly what it wants, since the facility probably does not
provide all possible useful combinations. Even if your private flavor has exactly the same
components as a preexisting flavor, it can still be useful since you can use its
:default-init-plist to select options of its component flavors and you can define one or
two methods to customize it just a little.

The operations described in this section are a standard protocol, which all
message-receiving objects are assumed to understand. The standard methods that implement
this protocol are automatically supplied by the flavor system unless the user specifically
tells it not to do so. These methods are associated with the flavor si:vanilla-flavor:

Unless you specify otherwise (with the :no-vanilla-flavor option to defflavor),
every flavor includes the vanilla flavor, which has no instance variables but
provides some basic useful methods. Note that this is in the system package, nicknamed si.

The object should output its printed-representation to a stream. The printer
sends this message when it encounters an instance or an entity. The arguments are the
stream, the current depth in list-structure (for comparison with *print-level*), and
whether escaping is enabled (a copy of the value of *print-escape*). si:vanilla-flavor
ignores the last two arguments and prints something like #<flavor-name
hexadecimal-address>. The flavor-name tells you what type of object it is and
the hexadecimal-address allows you to tell different objects apart.

The object should describe itself, printing a description onto the standard output
stream. The describe function sends this message when it encounters an instance.
si:vanilla-flavor outputs in a reasonable format the object, the name of its flavor, and
the names and values of its instance-variables. The instance variables are printed in
their order within the instance.

The object should return a list of the operations it can handle. si:vanilla-flavor
generates the list once per flavor and remembers it, minimizing consing and compute-time.
If the set of operations handled is changed, this list is regenerated the next time
someone asks for it.

operation is an operation name. The object should return the method it uses to
handle operation. If it has no handler for that operation, it should return nil.
This is like the get-handler-for function.

operation is an operation name and arguments is a list of arguments for the
operation. If the object handles the operation, it should send itself a message with that
operation and arguments, and return whatever values that message returns. If it doesn't
handle the operation it should just return nil.

The argument is a form that is evaluated in an environment in which special
variables with the names of the instance variables are bound to the values of the instance
variables. It works to setq one of these special variables; the instance variable is
modified. This is intended to be used mainly for debugging.

function is applied to args in an environment in which special variables with
the names of the instance variables are bound to the values of the instance variables. It
works to setq one of these special variables; the instance variable is modified. This is a
way of allowing callers to provide actions to be performed in an environment set up by the
instance.

When a flavor has or inherits more than one method for an operation, they must be
called in a specific sequence. The flavor system creates a function called a combined
method which calls all the user-specified methods in the proper order. Invocation of
the operation actually calls the combined method, which is responsible for calling the
others.

This example shows the default style of method combination, the one described in the
introductory parts of this chapter, called :daemon combination. Each style of method
combination defines which method types it allows, and what they mean. :daemon
combination accepts method types :before and :after, in addition to untyped
methods; then it creates a combined method which calls all the :before methods, only one
of the untyped methods, and then all the :after methods, returning the value of the
untyped method. The combined method is constructed by a function much like a macro's
expander function, and the precise technique used to create the combined method is what
gives :before and :after their meaning.

Note that the :before methods are called in the order foo, foo-mixin, bar-mixin and
foo-base. (foo-base does not have a :before method, but if it had one that one would be
last.) This is the standard ordering of the components of the flavor foo;
since it puts the base flavor last, it is called :base-flavor-last ordering. The :after
methods are called in the opposite order, in which the base flavor comes first. This is
called :base-flavor-first ordering.

Only one of the untyped methods is used; it is the one that comes first in
:base-flavor-last ordering. An untyped method used in this way is called a primary
method.

Other styles of method combination define their own method types and have their own
ways of combining them. Use of another style of method combination is requested with the
:method-combination option to defflavor. Here is an example which uses
:list method combination, a style of combination that allows :list methods and untyped
methods:

The :method-combination option in the defflavor for foo-base causes
:list method combination to be used for the :win operation on all flavors that have foo-base
as a component, including foo. The result is a combined method which calls all the
methods, including all the untyped methods rather than just one, and makes a list of the
values they return. All the :list methods are called first, followed by all the untyped
methods; and within each type, the :base-flavor-last ordering is used as specified. If the
:method-combination option said :base-flavor-first, the relative order of the :list
methods would be reversed, and so would the untyped methods, but the :list methods would
still be called before the untyped ones. :base-flavor-last is more often right, since it
means that foo's own methods are called first and si:vanilla-flavor's methods
(if it has any) are called last.

One method type, :default, has a standard meaning independent of the style
of method combination, and can be used with any style.

The default style of method combination. All the :before methods are called, then the
primary (untyped) method for the outermost flavor that has one is called, then all the
:after methods are called. The value returned is the value of the primary method.

Like the :daemon method combination style, except that the primary method
is wrapped in an :or special form with all :or methods. Multiple values can be returned
from the primary method, but not from the :or methods (as in the or special form). This
produces combined methods like the following:

This is useful primarily for flavors in which a mixin introduces an alternative to the
primary method. Each :or method gets a chance to run before the primary method and to
decide whether the primary method should be run or not; if any :or method returns a non-nil
value, the primary method is not run (nor are the rest of the :or methods).
Note that the ordering of the combination of the :or methods is controlled by the order
keyword in the :method-combination option.

Like the :daemon method combination style, except an or special form
is wrapped around the entire combined method with all :override typed methods before the
combined method. This differs from :daemon-with-or in that the :before and :after daemons
are run only if none of the :override methods returns non-nil. The
combined method looks something like this:

Calls all the methods inside a progn special form. Only untyped and
:progn methods are allowed. The combined method calls all the :progn methods and then all
the untyped methods. The result of the combined method is whatever the last of the methods
returns.

Calls all the methods inside an or special form. This means that each
of the methods is called in turn. Only untyped methods and :or methods are allowed; the
:or methods are called first. If a method returns a non-nil value, that value
is returned and none of the rest of the methods are called; otherwise, the next method is
called. In other words, each method is given a chance to handle the message; if it doesn't
want to handle the message, it can return nil, and the next method gets a
chance to try.

Calls each method with one argument; these arguments are successive elements of the
list that is the sole argument to the operation. Returns no particular value. Only untyped
methods and :inverse-list methods are allowed, etc.

If the result of a :list-combined operation is sent back with an :inverse-list-combined
operation, with the same ordering and with corresponding method definitions, each
component flavor receives the value that came from that flavor.

[Method-combination type]:pass-on

Note :pass-on method combination is not implemented in Allegro CL.

Calls each method on the values returned by the preceding one. The values returned by
the combined method are those of the outermost call. The format of the declaration in the defflavor
is:

where ordering is :base-flavor-first or :base-flavor-last. arglist may include the
&aux and &optional keywords.

Only untyped methods and :pass-on methods are allowed. The :pass-on methods are called
first.

[Method-combination type]:case

With :case method combination, the combined method automatically does a case dispatch
on the first argument of the operation, known as the suboperation. Methods of type
:case can be used, and each one specifies one suboperation that it applies to. If no :case
method matches the suboperation, the suboperation :otherwise is called. It receives the
suboperation keyword as its first argument, followed by the remaining arguments to the
original message. If no :otherwise method is defined, a default method is called that
signals an error.

:case methods are unusual in that one flavor can have many :case methods for the same
operation, as long as they are for different suboperations.

The suboperations :which-operations, :operation-handled-p, :send-if-handles and
:get-handler-for are all handled automatically based on the collection of :case methods
that are present.

Note: :send-if-handles and :get-handler-for are presently
unimplemented in Allegro CL.

Here is a list of all the method types recognized by the standard styles of method
combination:

[Method type]no method type

If no type is given to defmethod, a primary method is created. This is the most common
type of method.

[Method type]:before

[Method type]:after

These are used for the before-daemon and after-daemon methods used by :daemon method
combination.

[Method type]:default

If there are no untyped methods among any of the flavors being combined, then the
:default methods (if any) are treated as if they were untyped. If there are any untyped
methods, the :default methods are ignored.

Typically a base-flavor defines some default methods for certain of the operations
understood by its family. When using the default kind of method combination these default
methods are suppressed if another component provides a primary method.

[Method type]:or

[Method type]:and

These are used for :daemon-with-or and :daemon-with-and method combination. The :or
methods are wrapped in an or, and the :and methods are wrapped in an and, together with
the primary method, between the :before and :after methods.

[Method type]:override

Allows the features of :or method combination to be used together with
daemons. If you specify :daemon-with-override method combination, you may use :override
methods. The :override methods are executed first, until one of them returns non-nil.
If this happens, that method's value(s) are returned and no more methods are used. If all
the :override methods return nil, the :before, primary and :after methods are
executed as usual.

In typical usages of this feature, the :override method usually returns nil
and does nothing, but in exceptional circumstances it takes over the handling of the
operation.

[Method type]:case

Used by :case method combination.

[Method type]:or

[Method type]:and

[Method type]:progn

[Method type]:list

[Method type]:inverse-list

[Method type]:pass-on

[Method type]:append

[Method type]:nconc

[Method type]:sum

[Method type]:max

[Method type]:min

Each of these methods types is allowed in the method combination style of the same
name. In those method combination styles, these typed methods work just like untyped ones,
but all the typed methods are called before all the untyped ones.

The following four method types may appear with any method combination style; they have
standard meanings independent of the method combination style being used. Each is written
automatically by the flavor system, not the programmer.

[Method type]:wrapper

This is generated internally by defwrapper.

[Method type]:whopper

This is generated internally by defwhopper.

[Method type]:whopper-continuation

This is generated internally by defwhopper.

[Method type]:combined

This is generated internally for automatically-generated combined methods.

The most common form of combination is :daemon. One thing may not be clear: when do you
use a :before daemon and when do you use an :after daemon? In some cases the primary
method performs a clearly-defined action and the choice is obvious: :before :launch-rocket
puts in the fuel, and :after :launch-rocket turns on the radar tracking.

In other cases the choice can be less obvious. Consider the :init message, which is
sent to a newly-created object. To decide what kind of daemon to use, we observe the order
in which daemon methods are called. First the :before daemon of the instantiated flavor is
called, then :before daemons of successively more basic flavors are called, and finally
the :before daemon (if any) of the base flavor is called. Then the primary method is
called. After that, the :after daemon for the base flavor is called, followed by the
:after daemons at successively less basic flavors.

Now, if there is no interaction among all these methods, if their actions are
completely independent, then it doesn't matter whether you use a :before daemon or an
:after daemon. There is a difference if there is some interaction. The interaction we are
talking about is usually done through instance variables; in general, instance variables
are how the methods of different component flavors communicate with each other. In the
case of the :init operation, the init-plist can be used as well. The important thing to
remember is that no method knows beforehand which other flavors have been mixed in to form
this flavor; a method cannot make any assumptions about how this flavor has been combined,
and in what order the various components are mixed.

This means that when a :before daemon has run, it must assume that none of the methods
for this operation have run yet. But the :after daemon knows that the :before daemon for
each of the other flavors has run. So if one flavor wants to convey information to the
other, the first one should transmit the information in a :before daemon, and the
second one should receive it in an :after daemon. So while the :before daemons are
run, information is transmitted; that is, instance variables get set up. Then, when
the :after daemons are run, they can look at the instance variables and act on their
values.

In the case of the :init method, the :before daemons typically set up instance
variables of the object based on the init-plist, while the :after daemons actually do
things, relying on the fact that all of the instance variables have been initialized by
the time they are called.

The problems become most difficult when you are creating a network of instances of
various flavors that are supposed to point to each other. For example, suppose you have
flavors for buffers and streams, and each buffer should be accompanied by a
stream. If you create the stream in the :before :init method for buffers, you can inform
the stream of its corresponding buffer with an init keyword, but the stream may try
sending messages back to the buffer, which is not yet ready to be used. If you create the
stream in the :after :init method for buffers, there will be no problem with
stream creation, but some other :after :init methods of other mixins may have run and made
the assumption that there is to be no stream. The only way to guarantee success is to
create the stream in a :before method and inform it of its associated buffer by sending it
a message from the buffer's :after :init method. This scheme-creating associated objects
in :before methods but linking them up in :after methods-often avoids problems, because
all the various associated objects used by various mixins at least exist when it is time
to make other objects

Since flavors are not hierarchically organized, the notion of levels of abstraction is
not rigidly applicable. However, it remains a useful way of thinking about systems.

Wrappers and whoppers are complex features that you may not be able to
understand completely until you have gained some experience with flavors. Meanwhile, this
section can safely be skipped.

Sometimes the way the flavor system combines methods from different component flavors
is not sufficiently powerful. Notwithstanding the intricacies of method combination, each
component method is ultimately called directly from the combined method. No component
method ever calls another. It is therefore impossible for one component method to
communicate with others by binding specials, establishing catch handlers, unwind-protects,
or in general using any mechanism that has dynamic scope. It is true that with :daemon
combination :before and :after methods bracket the primary
method with regard to the time order of their execution, but the primary method is not run
within the dynamic scope of the daemons. Methods can, of course, communicate by setting
instance and special variables.

Wrappers and whoppers overcome this shortcoming by allowing a flavor to contribute
method code that is dynamically wrapped around execution of the rest of the
combined method. Perhaps you need to bind the value of *print-base* around
the execution of a combined method. Or perhaps you need to establish a catch around the
execution of the method so any component method can abort by executing a throw.
You may even want to bypass execution of the method completely unless certain conditions
are met. These are the kinds of things wrappers and whoppers can do. The difference
between the two is that a wrapper resembles a macro while a whopper is a function.

defwrapper defines a macro which expands into code to be wrapped
around the body of the combined method. The flavor system expands the wrapper macro at
flavor-combination time. The syntax is convoluted and may be clearer in an example.
Suppose you want to bypass execution of a method if the first argument is nil:

When the wrapper is expanded at flavor-combination time, it's wrapped-body
argument is bound to the combined method around which it is supposed to wrap. The wrapper
returns code with the combined method code body embedded inside; this example is typical
of macros in its use of backquote syntax. The wrapper should not assume anything about the
internal structure of the wrapped body. It should not try to destructure it or otherwise
look inside; doing so would involve implementation dependencies.

The form that the wrapper returns becomes the body of the new combined method. If there
are additional wrappers to be wrapped around this body, the returned form from the inner
wrapper becomes the wrapped-body argument when those outer wrappers are expanded.

It is worth emphasizing that all wrapper expansion happens at flavor-combination time,
not at message-send time. The lambda-list in the defwrapper form is the
lambda list to the generated code body at message-send time, not the lambda list to the
wrapper macro. Note that the planet and altitude arguments above are not
dereferenced with commas inside the wrapper definition body. They are not
flavor-combination time arguments to the wrapper macro; rather, they are arguments to the
combined method that receive their bindings only at message-send time.

The whopper is a regular function which is called at execution time when the message is
sent. It is passed a continuation which is the remaining body of the combined method
around which it wraps. It can call the continuation using either the continue-whopper
or lexpr-continue-whopper functions. (The mechanism for passing the
continuation is not visible to the user.) Usually a whopper will pass its continuation the
same arguments that it received, but it is legitimate for a whopper to alter the
arguments. Indeed, this is sometimes the whole purpose of a whopper.

This function is similar to continue-whopper except that the last argument is
treated as a list of arguments to be passed to the whopper continuation. The relation
between lexpr-continue-whopper and continue-whopper is
analogous to that between funcall and apply.

When the combined method is built, calls to all the before-daemon methods, primary
methods and after-daemon methods are combined together into a single body, and then
wrappers and whoppers are wrapped around them. Like daemon methods, wrappers and whoppers
work in outside-in order. If a wrapper or whopper is defined for a flavor built on other
component flavors, that wrapper or whopper goes around all wrappers and whoppers
contributed by inner components. If there are both wrappers and whoppers for a message,
the order in which they embed is controlled only by the inclusion order of the component
flavors; neither wrappers nor whoppers has priority for purposes of method combination. If
a single flavor contributes both a wrapper and a whopper, then that wrapper goes around
that whopper.

Whoppers are a somewhat newer feature than wrappers and generally preferred for most
uses. Since wrappers operate like macros, their code must appear in the expansion of each
combined method to which they apply. If the wrapper is mixed into many different flavors,
the many copies of the wrapper code can occupy a great deal of storage. Also, when a
wrapper is changed each combined method using it must be regenerated and recompiled.
Whoppers largely avoid these problems. Wrappers remain ever so slightly faster at
execution time, however, because a wrapper's body occurs inline in the combined method,
while a whopper incurs the overhead of two function calls, one to call the whopper itself,
and one to call its continuation.

An object that is an instance of a flavor is implemented as a hidden data type similar
to a simple vector. The zeroth slot points to a flavor descriptor, and successive
slots of the vector store the instance variables. Sometimes, for debugging, it is useful
to know that svref is legal on an instance. However, it is of course a
violation of the implicit contract with a flavor to use this fact in real code.

A flavor descriptor is a defstruct of type flavors::flavor. It is
also stored on the flavors::flavor property of the flavor name. It contains,
among other things, the name of the flavor, the size of an instance, the table of methods
for handling operations, and information for accessing the instance variables. The form

(describe-flavor flavor-name)

will print much of this information in readable format. defflavor
creates a flavor-descriptor for each flavor and links them together according to
the dependency relationships between flavors. Much of the information stored there, of
course, is not computed until flavor-combination time.

A message is sent to an instance simply by calling it as a function with the first
argument being the operation. The evaluator looks up the operation in the dispatch
hashtable stored in the flavor descriptor for that flavor and obtains a handler function
and a mapping table. It then binds self to the object, and replaces the message keyword on
the stack with the mapping table where it will be matched by an argument on the handler's
lambda list generated by defmethod. Finally, the handler function is
called. If there is only one method to be invoked, the handler function is that method;
otherwise it is an automatically-generated function, called the combined method, which
calls the component methods appropriately. If there are wrappers, they are incorporated
into the combined method. If there are any whoppers, a special variable is bound to a list
of whopper continuations where continue-whopper can find them.

The code body of each method function knows only about the instance variables declared
for its flavor, and this set of instance variables is known when the defining defmethod
is evaluated. However, the location of these instance variables within an instance of an
arbitrary flavor containing that flavor is not known until flavor-combination time. The
mapping table is used by a method to map the set of instance variables it knows about into
slot offsets within self. If all the component methods invoked by the
combined method derive from a single flavor, the mapping table obtained from the method
dispatch hashtable is a simple vector of slot numbers. If methods from more than one
component flavor are invoked from the combined method, then the mapping table is a vector
of vectors mapping each component flavor to its appropriate component mapping table, and
the combined method takes care of binding si:self-mapping-table appropriately
before calling each component.

For both interpreted and compiled methods in Allegro CL all instance variables are
lexical scoped within the body of the method. (This is different from the Franz Lisp
implementation, in which the interpreter cannot implement lexical scoping.)

There is a certain amount of freedom to the order in which you do defflavor's,
defmethod's, and defwrapper's. This freedom is designed to make
it easy to load programs containing complex flavor structures without having to do things
in a certain order. It is considered important that not all the methods for a flavor need
be defined in the same file. Thus the partitioning of a program into files can be along
modular lines.

The rules for the order of definition are as follows.

Before a method can be defined (with defmethod, defwrapper
or defwhopper) its flavor must have been defined (with defflavor).
This makes sense because the system has to have a place to remember the method, and
because it has to know the instance-variables of the flavor if the method is to be
compiled.

When a flavor is defined (with defflavor) it is not necessary that all
of its component flavors be defined already. This is to allow defflavors
to be spread between files according to the modularity of a program, and to provide for
mutually-dependent flavors. Methods can be defined for a flavor some of whose component
flavors are not yet defined; however, compilation of a method which refers to instance
variables inherited from a flavor not yet defined, and not mentioned in a :required-instance-variable
clause, will produce a compiler warning that the variable was declared special (because
the system did not realize it was an instance variable). If this happens, you should fix
the problem and recompile. It may be sufficient just to change the order in which the
flavors are defined, but considerations of modularity, clarity, and self documentation
make it far preferable to insert :required-instance-variable clauses.

The methods automatically generated by the :gettable-instance-variables, :settable-instance-variables,
and :outside-accessible-instance-variablesdefflavor options
are generated at the time the defflavor is done.

The first time a flavor is instantiated, or when compile-flavor-methods is done, the
system looks through all of the component flavors and gathers various information. At this
point an error is signaled if not all of the components have been defflavor'ed.
This is also the time at which certain other errors are detected, such as the lack of a
required instance-variable (see the :required-instance-variables option to defflavor).
The ordered set of instance variables is determined and their slots assigned within an
instance. The combined methods are generated unless they already exist and are correct.
The flavor system tries very hard never to re-defun a combined method
unless its contents actually must change.

After a flavor has been instantiated, it is possible to make changes to it. If
possible, such changes affect all existing instances. This is described more fully
immediately below.

You can change anything about a flavor at any time. You can change the flavor's general
attributes by doing another defflavor with the same name. You can add or
modify methods by doing defmethod's. If you do a defmethod
with the same flavor-name, operation (and suboperation if any), and (optional) method-type
as an existing method, that method is replaced by the new definition.

These changes always propagate to all flavors that depend upon the changed flavor.
Normally the system propagates the changes to all existing instances of the changed flavor
and its dependent flavors. However, this is not possible when the flavor has been changed
in such a way that the old instances would not work properly with the new flavor. This
happens if you change the number of instance variables, which changes the size of an
instance. It also happens if you change the order of the instance variables (and hence the
storage layout of an instance), or if you change the component flavors (which can change
several subtle aspects of an instance). The system does not keep a list of all the
instances of each flavor, so it cannot find the instances and modify them to conform to
the new flavor definition. Instead it gives you a warning message to the effect that the
flavor was changed incompatibly and the old instances will not get the new version. The
system leaves the old flavor data-structure intact (the old instances continue to point at
it) and makes a new one to contain the new version of the flavor. If a less drastic change
is made, the system modifies the original flavor data-structure, thus affecting the old
instances that point at it. However, if you redefine methods in such a way that they only
work for the new version of the flavor, then trying to use those methods with the old
instances won't work.

There is one circumstance where it is impossible for the system to propagate changes
completely and automatically. Recall that method functions need to know the ordered set of
instance variables in order to look up instance variables in the mapping table. For
interpreted code, this list is retrieved from the flavor descriptor at eval time, so if
the order changes, the method still works. (It will fail, of course, if an instance
variable to which it refers disappears completely.) However, when methods are compiled,
instance variable indices into the mapping table are determined at compile time. (This
enhances speed.) Thus, if the list of instance variables known to a flavor ever changes,
it will be necessary to recompile (or at least re-defmethod) all compiled
methods for the flavor that referenced any instance variables.

It is often useful to associate a property list with an abstract object, for the same
reasons that it is useful to have a property list associated with a symbol. This section
describes a mixin flavor, si:property-list-mixin, that can be used as a component of any
new flavor in order to provide that new flavor with a property list. For more details and
examples, see the general discussion of property lists. The usual property list
functionalities (get, putprop, etc.) are obtained by sending the instance the
corresponding message. The contents of the property list can be initialized by providing a
:property-list init option on the init-plist given to instantiate-flavor.

Like the :get operation, except that the argument is a list of
property names. The :getl operation searches down the property list until
it finds a property whose property name is one of the elements of property-name-list.
It returns the portion of the property list beginning with the first such property that it
found. If it doesn't find any, it returns nil.

Removes the object's property-name property, by splicing it out of the
property list. It returns one of the cells spliced out, whose car is the former value of
the property that was just removed. If there was no such property to begin with, the value
is nil.

The property-name property of the object should be a list (note that nil
is a list and an absent property is nil). This operation sets the property-name
property of the object to a list whose car is value and whose cdr is the former property-name
property of the list. This is analogous to doing

There are no built-in techniques to copy instances because there are too many questions
raised about what should be copied. These include:

Do you or do you not send an :init message to the new instance? If you do,
what init-plist options do you supply?

If the instance has a property list, you should copy the property list (e.g. with copylist)
so that putprop or remprop on one of the instances does
not affect the properties of the other instance.

If the instance is a port connected to a network, some of the instance variables
represent an agent in another host elsewhere in the network. Should the copy talk to the
same agent, or should a new agent be constructed for it?

If the instance is a port connected to a file, should copying the stream make a copy of
the file or should it make another stream open to the same file? Should the choice depend
on whether the file is open for input or for output?

In general, you can see that in order to copy an instance one must understand a lot
about the instance. One must know what the instance variables mean so that the values of
the instance variables can be copied if necessary. One must understand what relations to
the external environment the instance has so that new relations can be established for the
new instance. One must even understand what the general concept `copy' means in the
context of this particular instance, and whether it means anything at all.

Copying is a generic operation, whose implementation for a particular instance depends
on detailed knowledge relating to that instance. Modularity dictates that this knowledge
be contained in the instance's flavor, not in a general copying function. Thus the
way to copy an instance is to send it a message, as in (send object :copy). It is up to
you to implement the operation in a suitable fashion, such as

The flavor system chooses not to provide any default method for copying an instance,
and does not even suggest a standard name for the copying message, because copying
involves so many semantic issues.

If a flavor supports the :reconstruction-init-plist operation, a suitable
copy can be made by invoking this operation and passing the result to make-instance
along with the flavor name. This is because the definition of what the :reconstruction-init-plist
operation should do requires it to address all the problems listed above. Implementing
this operation is up to you, and so is making sure that the flavor implements sufficient
init keywords to transmit any information that is to be copied.