Google
 

Trailing-Edge - PDP-10 Archives - clisp - clisp/flavors/flavors.doc
There are no other files named flavors.doc in the archive.








                            INTRODUCTION TO FLAVORS





































Copyright (C) 1985 This document is adapted from text written primarily by Davi
       Daniel Weinreb which appeared in the Lisp Machine Manual, fourth
      edition, chapter 20, copyrighted by the Massachusetts Institute of
     Technology.  We have used it with permission of the authors and MIT.


1. Introduction
  Object  oriented  programming is available in Common Lisp.  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  attempts  to
explain  what  programming  with  objects  and  with message passing means, the
various means of implementing these in Common Lisp, and  when  you  should  use
them.  It assumes no prior knowledge of any other languages.
2. Objects
  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 with which "piece of text" the window is associated.
  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 object (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  object, and the operations that can be
performed on any of the instances of each type.

  This should not be wholly unfamiliar to the reader.  Earlier in this  manual,
we  saw  a  few  examples  of  this  kind  of programming.  A simple example is
disembodied property lists, and the functions get, putprop, and remprop.    The
disembodied  property  list  is  a type of object; you can instantiate one with
(cons nil nil) (that  is,  by  evaluating  this  form  you  can  create  a  new
disembodied  property  list);  there are three operations on the object, namely
get, putprop, and remprop.  Another example in the manual was the first example
of the use of defstruct, which was called  a  ship.    defstruct  automatically
defined  some operations on this object: the operations to access its elements.
We could define other functions that did useful  things  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 property list case,  the  Lisp  object  is  a  list  with
alternating  indicators  and  values;  in  the ship case, the Lisp object is an
array 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.  get examines  the
state  of  a property list, and putprop alters it; ship-x-position examines the
state of a ship, and (setf (ship-mass 5.0)) alters it.

  We have now seen the essence of object-oriented programming.    A  conceptual
object  is  modelled  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.
3. Modularity
  An  important benefit of the object-oriented style is that it lends itself to
a particularly simple and lucid kind  of  modularity.    If  you  have  modular
programming constructs and techniques available, it helps and encourages 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  program  knows  what  the  facility's
external interfaces guarantee to do, 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 only depends on the external
definition of these operations: it knows that if it putprops  a  property,  and
doesn't  remprop  it  (or  putprop  over it), then it can do get and be sure of
getting back the same thing it put in.  The important thing about  this  hiding
of  the  details  of  the implementation is that someone reading a program that
uses disembodied property lists need not concern  himself  with  how  they  are
implemented; he need only understand what they undertake to do.  This saves the
programmer   a   lot  of  time,  and  lets  him  concentrate  his  energies  on
understanding the program he is working on.   Another  good  thing  about  this
hiding  is  that the representation of property lists could be changed, and the
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:
(defstruct (ship)
  ship-x-position
  ship-y-position
  ship-x-velocity
  ship-y-velocity
  ship-mass)

(defun ship-speed (ship)
  (sqrt (+ (^ (ship-x-velocity ship) 2)
           (^ (ship-y-velocity ship) 2))))

(defun ship-direction (ship)
  (atan (ship-y-velocity ship)
        (ship-x-velocity ship)))

  The  caller  need  not  know  that  the  first  two  functions were structure
accessors and that the second two were  written  by  hand  and  do  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:

(defstruct (ship)
  ship-x-position
  ship-y-position
  ship-speed
  ship-direction
  ship-mass)

(defun ship-x-velocity (ship)
  (* (ship-speed ship) (cos (ship-direction ship))))

(defun ship-y-velocity (ship)
  (* (ship-speed ship) (sin (ship-direction ship))))

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 works, because 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  identity  (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 (aref 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 which 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  aref.
A  caller  who  does  so  is in error; he is depending on 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, ship may get reimplemented
overnight, and the code that  does  the  aref  will  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  is  what
specifies the interface between the two modules.

  Unlike some other languages that provide abstract types, Common Lisp 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" programs and the "user" programs in
Lisp; users are allowed to get into 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 entrypoints 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 important thing is not
that they are functions--in Lisp  everything  is  done  with  functions.    The
important thing is that we have defined a new conceptual operation and given it
a  name, rather than requiring anyone 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).

  It is just as true  of  such  abstract-operation  functions  as  of  ordinary
functions  that  sometimes  they are simple enough that we want the compiler 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,  defsubsts,  or  optimizers.
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  whatever);  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 is something we sometimes
accept for the sake of efficiency.

  In the present implementation of flavors, which is discussed below, there  is
no  such  compiler incorporation of nonmodular knowledge into a program, except
when the "outside-accessible instance  variables"  feature  is  used;  see  the
section on the outside-accessible-instance-variables option, where this problem
is  explained  further.    If  you  don't  use the "outside-accessible instance
variables" feature, you don't have to worry about this.
4. Generic Operations
  Suppose  we  think  about  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 the attributes apply  to.    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 will need to know the ship's attributes,
and will have to 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 most  programming  languages,
including  Common  Lisp.   The + function will accept fixnums, flonums, ratios,
bignums, etc., and perform the appropriate type of addition based on  the  data
types  of  the  objects  being  manipulated.  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.

  A  terminology  for  the  use of such generic operations has emerged from the
Smalltalk and Actor languages: performing a generic operation is called sending
a message.  The objects in the program are thought of as little people, who get
sent messages and respond with answers.  In the example above, the objects  are
sent  x-position  messages,  to which they respond with their x position.  This
message passing is how generic operations are performed.

  Sending a message is a way of invoking a function.  Along with  the  name  of
the  message,  in  general,  some arguments are passed; when the object is done
with the message, some values are returned.   The  sender  of  the  message  is
simply  calling  a  function with some arguments, and getting some values back.
The interesting thing is that  the  caller  did  not  specify  the  name  of  a
procedure  to  call.   Instead, it specified a message name and an object; that
is, it said what operation to perform, and what object to perform it on.    The
function to invoke was found from this information.

  When  a  message  is sent to an object, a function therefore must be found to
handle the message.  The two data used to figure out which function to call are
the type of the object, and the name of the message.  The same set of functions
are 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 name  are  data  which  are  passed  as  arguments  to  the
function,  so  the  name  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
kind  of  message  to  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  named  x-position
(with  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.
5. Generic Operations in Lisp
  How  do  we  implement  message passing in Lisp?  By convention, objects that
receive messages are always functional objects (that is, you can apply them  to
arguments),  and  a  message  is  sent to an object by calling that object as a
function, passing the name of the  message  as  the  first  argument,  and  the
arguments  of  the  message  as  the  rest of the arguments.  Message names are
represented by symbols; normally these symbols are in the keyword package since
messages are a protocol for communication between different programs, which may
reside 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:
(funcall my-ship ':x-position)
  This form returns the x position as its returned value.  To set the ship's  x
position to 3.0, we send it a message like this:
(funcall my-ship ':set-x-position 3.0)

  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 a message name.  The object must consider this message  name,
find  the  function  which is the method for that message name, and invoke that
function.

  This raises the question of how message receiving works.    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; objects can't just  be  defstructs
any more, since those aren't functions.  But the structure defined by defstruct
was doing something useful: it was holding the instance variables (the internal
state) of the object.  We need a function with internal state; that is, we need
a coroutine.

  Of  the  features  presented  so far, 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 selectq
form to dispatch on its first argument.

  While using closures does work, it has several serious problems.    The  main
problem is that in order to add a new operation to a system, it is necessary to
modify  a  lot  of  code;  you  have to find all the types that understand that
operation, and add a new clause to the selectq.  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.

  The  conventional way of making generic operations is to have a procedure for
each operation, which has a big selectq for all the types; this means you  have
to  modify  code to add a type.  The way described above is to have a procedure
for each type, which has a big selectq for all the operations; this  means  you
have  to  modify  code  to  add an operation.  Neither of these has the desired
property that extending the system should only require adding code, rather than
modifying code.

  Closures are also somewhat  clumsy  and  crude.    A  far  more  streamlined,
convenient,  and powerful system for creating message-receiving objects exists;
it is called the Flavor mechanism.  With flavors, you  can  add  a  new  method
simply  by  adding  code, without modifying anything.  Furthermore, many common
and useful things to do are very easy to do with flavors.   The  rest  of  this
chapter describes flavors.
6. Simple Use of 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.
(defflavor ship (x-position y-position
                 x-velocity y-velocity mass)
                ()
  :gettable-instance-variables)

(defmethod (ship :speed) ()
  (sqrt (+ (^ x-velocity 2)
           (^ y-velocity 2))))

(defmethod (ship :direction) ()
  (atan y-velocity x-velocity))

  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 are the five that 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
message  is  a symbol with the same name as the instance variable, but interned
on the keyword package.  Thus, methods  are  created  to  handle  the  messages
: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  messages  named  :speed.    The  second
subform  is  the  lambda-list,  and  the  rest is the body of the function that
handles the :speed message.   The  body  can  refer  to  or  set  any  instance
variables  of  the  flavor,  the same as it can with local variables or special
variables.  When any instance of the  ship  flavor  is  invoked  with  a  first
argument  of  :direction, the body of the second defmethod will be 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 when
the arguments of atan are evaluated, the values of instance  variables  of  the
object  to which the message was sent will be used as the arguments.  atan will
be invoked, and the result it returns will be returned by the instance itself.

  Now we have seen how to create a new abstract type:  a  new  flavor.    Every
instance  of  this  flavor  will  have 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 will return an object whose printed representation is:
#<SHIP 1213731>

  (Of course, the value of the magic number will vary; it  is  not  interesting
anyway.)    The  argument  to make-instance is, as you can see, 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.

  Examination of the flavor we have defined shows that it is quite  useless  as
it stands, since there is no way to set any of the parameters.  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  messages
named  :set-x-position,  :set-y-position, and so on; each such method takes one
argument, and sets the corresponding instance variable to the given value.

  Another option we can add to the defflavor  is  :initable-instance-variables,
to allow 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 makes 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 set of init options are sometimes called
the init-plist because they are like a property list.

  Here is the improved defflavor:
(defflavor ship (x-position y-position
                 x-velocity y-velocity mass)
                ()
  :gettable-instance-variables
  :settable-instance-variables
  :initable-instance-variables)

  All  we  have  to  do is evaluate this new defflavor, and the existing flavor
definition will be updated and now include the new methods  and  initialization
options.    In  fact, the instance we generated a while ago will now be able to
accept these new messages!  We can set the mass  of  the  ship  we  created  by
evaluating
(funcall my-ship ':set-mass 3.0)
and the mass instance variable of my-ship will properly get 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  (describe my-ship) at this point, the
following would be printed:
#<SHIP 13731210>, an object of flavor SHIP,
 has instance variable values:
        X-POSITION:         unbound
        Y-POSITION:         unbound
        X-VELOCITY:         unbound
        Y-VELOCITY:         unbound
        MASS:               3.0

  Now that the instance variables are "initable", we can  create  another  ship
and  initialize  some of the instance variables using the init-plist.  Let's do
that and describe the result:
(setq her-ship (make-instance 'ship ':x-position 0.0
                                    ':y-position 2.0
                                    ':mass 3.5))
                ==> #<SHIP 13756521>


(describe her-ship)
#<SHIP 13756521>, an object of flavor SHIP,
 has instance variable values:
        X-POSITION:         0.0
        Y-POSITION:         2.0
        X-VELOCITY:         unbound
        Y-VELOCITY:         unbound
        MASS:               3.5

  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:
(defvar *default-x-velocity* 2.0)
(defvar *default-y-velocity* 3.0)

(defflavor ship ((x-position 0.0)
                 (y-position 0.0)
                 (x-velocity *default-x-velocity*)
                 (y-velocity *default-y-velocity*)
                 mass)
                ()
  :gettable-instance-variables
  :settable-instance-variables
  :initable-instance-variables)

(setq another-ship (make-instance 'ship ':x-position 3.4))

(describe another-ship)
#<SHIP 14563643>, an object of flavor SHIP,
 has instance variable values:
        X-POSITION:         3.4
        Y-POSITION:         0.0
        X-VELOCITY:         2.0
        Y-VELOCITY:         3.0
        MASS:               unbound

  x-position   was   initialized   explicitly,  so  the  default  was  ignored.
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.  mass was not explicitly  initialized  and  did
not have a default initialization, so it was left unbound.

  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 chapter.  But even with the small set of
features we have seen so far, it is easy to write object-oriented programs.
7. Mixing Flavors
  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 defmethods that looked just like those of ship, and then
meteors  and ships would both accept the same operations.  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:
(defflavor ship (engine-power number-of-passengers name)
                (moving-object)
   :gettable-instance-variables)

(defflavor meteor (percent-iron) (moving-object)
   :initable-instance-variables)

  These defflavor forms use the second subform, which  we  ignored  previously.
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 things 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  will still accept the :x-velocity and :speed messages, and they will
do the same thing.  However, the :engine-power message will also be  understood
(and will return 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, and eliminating duplicates.  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; if there is  a  cycle  in  the  directed
graph, it will 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 will have an instance
variable named foo, and any methods that refer to foo will refer to  this  same
instance  variable.  Thus different components of a flavor can communicate with
one another using shared instance variables.  (Typically,  only  one  component
ever  sets  the variable, and 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 message supported by the  flavor.    This  function  is
constructed  out of all the methods for that message 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
a message by providing your own method.  Your method will be called, and  foo's
will never be called.

  Simple  overriding is often useful; if you want to make a new flavor bar that
is just like foo  except  that  it  reacts  completely  differently  to  a  few
messages,  then  this  will  work.  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 message, 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.

  When  methods  are  combined, a single primary method is found; it comes from
the first component flavor that has one.   Any  primary  methods  belonging  to
later component flavors are ignored.  This is just what we saw above; bar could
override foo's primary method by providing its own primary method.

  However,  you  can  define  other  kinds  of methods.  In particular, you can
define daemon methods.  They come in two kinds, before and after.  There  is  a
special  syntax  in defmethod for defining such methods.  Here is an example of
the syntax.  To give the ship flavor an  after-daemon  method  for  the  :speed
message, the following syntax would be used:

(defmethod (ship :after :speed) ()
   body)

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  will
run  before  any of those in foo, and bar's after-daemons will 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 it should not matter what flavor-2 is
built out of.  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 message is a stream (we can ignore the
others for now), and the receiver of the  message  is  supposed  to  print  its
printed  representation  on  the 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 message, then that method will take over the
job  of printing completely; vanilla-flavor's method will not be called at all.
However, if we give ship a before-daemon method for  the  :print-self  message,
then  it will get invoked before the vanilla-flavor message, 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 on method-combination.  The vanilla-flavor and what it  does  for
you are also explained later in a section on the vanilla flavor.
8. The Instance Datatype
  Various  parts  of what are considered Flavors require hooks into the system.
Portable code should attempt not to use any of the features mentioned  in  this
section,  with  the exception of :print-self, which can be portably implemented
using defstruct.
  One traditional hook is into the type system.  (type-of instance) returns the
flavor that the instance was made from.  typep instance flavor) returns  T  iff
the  instance's  flavor  has  the  flavor  as  a component.  Lastly, (instancep
instance) <=> (typep instance 'instance) returns T. 

  Various lisp operations on an instance should simply send a  message  to  the
object.  It has been proposed that the system should only send the names of the
functions,   rather   than  any  keyworded  symbol,  but  vanilla-flavor  could
translate.  Describe of an  instance  may  send  a  :describe  message  to  the
instance.   More esoteric hooks would be having the system send a :fasd-form or
some such message to get a form that, when loaded and evaluated, would  make  a
copy  of the object; and having eval of an instance send an EVAL message to the
object.
9. Flavor Functions
*all-flavor-names*                                                   [Variable]
                This  is  a list of the names of all the flavors that have ever
                been defflavored.

*undefined-flavor-names*                                             [Variable]
                This is a list of all flavors which have been referred  to  but
                not defined.

*flavor-compile-methods*                                             [Variable]
                If this variable is non-nil, combined methods are automatically
                compiled.

*dirty-flavors*                                                      [Variable]
                A stack (implemented as a vector) of unclean flavor names; that
                is, those which need to be updated.

defflavor name ({var}*) ({flavor}*) {option}*                           [Macro]
                Flavor-name  is  a  symbol  which  serves  to name this flavor.
                Traditionally the flavor  definition  is  a  defstruct  on  the
                flavor property of the name.

                (typep  obj),  where  obj  is  an  instance of the flavor named
                flavor-name, will return the symbol flavor-name.    (typep  obj
                flavor-name)  is  t  if  obj is an instance of a flavor, one of
                whose components (possibly itself) is flavor-name.

                var1, var2,  etc.  are  the  names  of  the  instance-variables
                containing the local state for this flavor.  A list of the name
                of  an  instance-variable  and a default initialization form is
                also acceptable; the initialization form will be 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 will remain unbound.

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

                opt1, opt2, etc. are options;  each  option  may  be  either  a
                keyword  symbol  or  a  list of a keyword symbol and arguments.
                The options to  defflavor  are  described  in  the  section  on
                defflavor-options.

defmethod (name method-type message) lambda-list {form}*                [Macro]
                Defines  a  method,  that is, a function to handle a particular
                message  sent  to  an  instance   of   a   particular   flavor.
                Flavor-name  is  a symbol which is the name of the flavor which
                is to receive the method.  Method-type is a keyword symbol  for
                the  type  of  method;  it  is  omitted when you are defining a
                primary method, which is the usual case.  Message is a  keyword
                symbol which names the message to be handled.

                The  meaning  of  the  method-type  depends  on  what  kind  of
                method-combination is declared for this message.  For instance,
                for daemons :before and :after are allowed.  See the section on
                method combination for a complete description of  method  types
                and 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
                message  keyword,  is  automatically  handled, and so it is not
                included in the  lambda-list.    form1,  form2,  etc.  are  the
                function body; the value of the last form is returned.

                If  you  redefine  a  method  that  is already defined, the old
                definition is replaced by the new  one.    Given  a  flavor,  a
                message  name,  and  a  method  type,  there  can  only  be one
                function, so if you define a :before daemon method for the  foo
                flavor  to  handle  the  :bar  message,  then  you  replace the
                previous before-daemon; however, you do not affect the  primary
                method or methods of any other type, message name or flavor.

make-instance name {init-option value}                               [Function]
                This  creates  and returns an instance of the specified flavor.
                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.  If the flavor supports the :init message,
                it is sent to the newly-created object with one  argument,  the
                init-plist.  This is a disembodied property-list containing the
                init-options  specified  and  those defaulted from the flavor's
                :default-init-plist.

send object message-name &rest arguments                             [Function]
                Finds the appropriate handler for the message  and  invokes  it
                with     the    given    arguments,    and    some    additiona
                implementation-dependent arguments.

defwrapper (flavor message) (({argname}*) . bodyname) . body            [Macro]
                This is hairy and if you don't understand it  you  should  skip
                it.

                Sometimes  the  way  the  flavor system combines the methods of
                different flavors (the daemon system) is not  powerful  enough.
                In  that  case  defwrapper  can be used to define a macro which
                expands into code which is wrapped around the invocation of the
                methods.  This is best explained by  an  example;  suppose  you
                needed  a lock locked during the processing of the :foo message
                to the bar flavor, which takes two arguments, and  you  have  a
                lock-frobboz  special-form  which  knows  how  to lock the lock
                (presumably it  generates  an  unwind-protect).    lock-frobboz
                needs  to  see  the first argument to the message; perhaps that
                tells it what sort of operation is going to be performed  (read
                or write).
                (defwrapper (bar :foo) ((arg1 arg2) . body)
                  `(lock-frobboz (self arg1)
                     . ,body))
                The  use  of  the body macro-argument prevents the defwrappered
                macro from knowing the exact implementation and allows  several
                defwrappers from different flavors to be combined properly.

                Note  well  that the argument variables, arg1 and arg2, are not
                referenced with commas  before  them.    These  may  look  like
                defmacro  "argument"  variables,  but  they  are  not.    Those
                variables are not bound  at  the  time  the  defwrapper-defined
                macro  is  expanded  and  the  back-quoting is done; rather the
                result of that macro-expansion and back-quoting is code  which,
                when  a  message  is  sent,  will  bind  those variables to the
                arguments in the message as local  variables  of  the  combined
                method.

                Consider  another  example.    Suppose you thought you wanted a
                :before daemon, but found that if  the  argument  was  nil  you
                needed  to  return  from  processing  the  message immediately,
                without executing the  primary  method.    You  could  write  a
                wrapper such as
                (defwrapper (bar :foo) ((arg1) . body)
                  `(cond ((null arg1))          ;Do nothing if arg1 is nil
                         (t before-code
                            . ,body)))

                Suppose you need a variable for communication among the daemons
                for  a  particular  message; perhaps the :after daemons need to
                know what the primary method did,  and  it  is  something  that
                cannot  be  easily  deduced from just the arguments.  You might
                use an instance variable  for  this,  or  you  might  create  a
                special  variable  which  is bound during the processing of the
                message and used free by the methods.
                (defvar *communication*)
                (defwrapper (bar :foo) (ignore . body)
                  `(let ((*communication* nil))
                     . ,body))

                Similarly you might want a wrapper which puts a  *catch  around
                the  processing  of  a  message  so that any one of the methods
                could throw out in the event of an unexpected condition.

                Like daemon methods, wrappers work in  outside-in  order;  when
                you  add  a  defwrapper to a flavor built on other flavors, the
                new wrapper is placed outside any  wrappers  of  the  component
                flavors.    However,  all  wrappers  happen  before any daemons
                happen.  When the combined method is built, the  calls  to  the
                before-daemon   methods,   primary  methods,  and  after-daemon
                methods are all placed together,  and  then  the  wrappers  are
                wrapped  around  them.    Thus, if a component flavor defines a
                wrapper, methods added by new flavors will execute within  that
                wrapper's context.

defwhopper (flavor [type] message) arglist . body                [Special Form]
                Arglist  is  the  same  as  the  argument  list  for any method
                handling message.  The default type is :whopper.

                Whoppers are to functions as wrappers are to macros.  Code  for
                wrappers  might  be duplicated many times, and so whoppers were
                devised to save space.  Note that to do this, the whopper  code
                is a function, and so must not only be called, but it must call
                a continuation function, for a net cost of two function calls.

                Whoppers  have  three  forms  that  they  may  use  in order to
                continue the combined method:

continue-whopper &rest args                                             [Macro]
                Calls the methods for the message that the whopper is handling.
                Args is the list of arguments sent.  This only works  inside  a
                whopper.    The  whopper  may  change the arguments rather than
                passing those it receives verbatim.

lexpr-continue-whopper &rest args                                       [Macro]
                Uses  apply  instead  of  funcall  to  call  the   continuation
                function.

continue-whopper-all                                                 [Function]
                This  performs  a  whopper  continuation  and simply passes the
                arguments it gets all on the the methods.  This avoids  consing
                a rest argument.

  Whoppers  may  be considered a kind of wrapper, for the purposes of ordering.
If a flavor defines both a wrapper and a  whopper,  though,  the  wrapper  goes
outside the whopper.

undefmethod (flavor [type] message)        [Macro] Generic undefining form.  To
                undefine a wrapper, use it with :wrapper as  the  method  type.
                For whoppers, use type :whopper.

self                                                                 [Variable]
                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).  Note that this is a lexical variable, not
                a special.
recompile-flavor name &optional message (do-dependents t)
                Used   to  recalculate  the  combined  methods  for  a  flavor.
                Generally this is done for you automatically,  but  if  a  user
                macro  or other such information unknown to the system changes,
                you may want to recalculate the  combined  methods  explicitly.
                It's also possible that the system may miss a wrapper change if
                it  just  hashes the body, in which case you'll have to do this
                manually.  recompile-flavor  only  affects  flavors  that  have
                already been compiled.  Typically this means it affects flavors
                that have been instantiated, but does not bother with mixins.

compile-flavor flavor                                                [Function]
                Prepares the named flavor for instantiation; mainly, this means
                it calculates the combined methods for the flavor.

compiler-compile-flavors &rest flavors                                  [Macro]
                When  placed  in  a  file  that  defines  some instantiable (or
                abstract) flavors, includes the code for the combined  methods.
                It  also makes sure that everything referred to by the combined
                method is present in the  loadtime  environment  (assuming  the
                same  flavor  structure).  Also, when the sfasl file is loaded,
                all the other structures required for  instantiation  will  get
                generated.  flavor) will get generated.

                This  means  that  the combined methods get compiled at compile
                time, and the data  structures  get  generated  at  load  time,
                rather  than both things happening at run time.  This is a very
                good thing to use, since the need to  invoke  the  compiler  at
                run-time  makes  programs  that use flavors slow the first time
                they  are  run.    (The  compiler  will  still  be  called   if
                incompatible  changes  have  been  made,  such  as  addition or
                deletion of methods that must be called by a combined method.)

                You should only use compiler-compile-flavors for  flavors  that
                are  going to be instantiated.  For a flavor that will never 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 all inherit the combined methods
                of this flavor instead of each one having its  own  copy  of  a
                combined method which happens to be identical to the others.

                The compiler-compile-flavors forms should be compiled after all
                of  the  information  needed  to create the combined methods is
                available.  You  should  put  these  forms  after  all  of  the
                definitions  of  all relevant flavors, wrappers, and methods of
                all components of the flavors mentioned.

get-handler-for object message   [Function] Given an object and a message, will
                return  that object's method for that message, 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 only an informational device.

flavor-allowed-init-keywords flavor                                  [Function]
                Returns a list of all symbols that are valid init  options  for
                flavor, sorted alphabetically.

flavor-allows-init-keyword-p name keyword                            [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
                which contributes the support of that keyword.

symeval-in-instance instance symbol &optional no-error-p             [Function]
                This function is used to find the value of an instance variable
                inside a particular instance.  Instance is the instance  to  be
                examined,  and  symbol  is  the  instance  variable whose value
                should be returned.  If there is no such instance variable,  an
                error  is signalled, unless no-error-p is non-nil in which case
                nil is returned.

set-in-instance instance symbol value                                [Function]
                This function is  used  to  alter  the  value  of  an  instance
                variable inside a particular instance. Instance is the instance
                to  be  altered,  symbol  is  the instance variable whose value
                should be set, and value is the new value.  If there is no such
                instance variable, an error is signalled.
10. Defflavor Options
  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.   Each option can be written in two forms; either the keyword by itself,
or a list of the keyword and "arguments" to that keyword.
  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 component flavors; just the ones mentioned  in  this  flavor's
defflavor.  When 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  signalled.    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.

:Gettable-instance-variables causes a  method  :x  to  be  generated  for  each
                instance variable x.  :X gets the value of x.

:initable-instance-variables  creates  a  :x  init-option for make-instance for
                each instance variable x.

:Settable-instance-variables causes a method :set-x to be  generated  for  each
                instance  variable  x.    :Set-x  sets  the  value  of x to the
                supplied    value.          Using     this     option     cause
                :gettable-instance-variables  and  :initable-instance-variables
                to take effect.

:Init-keywords should be a list of all keywords accepted by the flavor's  :init
                handler,  if  it  has  one.    This is used for error handling;
                before the :init message is sent the flavors system checks that
                all of the keywords in the init-plist are either members of the
                init-keywords list or initable instance variables.

:default-init-plist takes arguments which 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.  :Default-init-plist
                entries that initialize instance variables are not added to the
                init-plist seen by the :init methods.

:required-instance-variables declares that any flavor  incorporating  this  one
                which 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 is not one of those which checks  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 will take care of 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.

:required-init-keywords takes as arguments a list of init keywords that must be
                supplied.  It is an error to try to make an  instance  of  this
                flavor  or  a  flavor  which  depends on it unless you specifiy
                these keywords to make-instance  or  as  a  :default-init-plist
                option in some component flavor.

:required-methods  takes  as  arguments  names  of  messages  which  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  messages.    Typically  this  option
                appears  in  the  defflavor for a base flavor.  Usually this is
                used when a base flavor does a funcall-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, bui
                only with other components (mixins) that do handle the message.
                This keyword allows the error of  having  no  handler  for  the
                message  be  detected when the flavor is defined (which usually
                means at compile time) rather than at run time.

:required-flavors takes as arguments a list of flavors which any  flavor  built
                on  this  one must include (possibly indirectly) as components.
                This is different from listing the flavors as component flavors
                in that required flavors are not specified  to  appear  in  any
                particular place in the component flavors list.  If you require
                a  flavor you allow all instance variables which it declares to
                be accessed, and cause  an  error  to  be  signalled  when  you
                instantiate a flavor which doesn't include the requred flavor.

                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,
                (defflavor relativity-mixin () (moving-object))
                (defmethod (relativity-mixin :mass) ()
                  (/ mass (sqrt (- 1 (expt (/ (funcall-self ':speed)
                                              *speed-of-light*)
                                           2)))))
                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 caller, for
                example:
                (defflavor starship ()
                           (relativity-mixin long-distance-mixin ship))
                which puts 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
                instance variables such as mass (the rest mass), but  does  not
                specify any place for moving-object in the list of components.

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

:included-flavors 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 that includes it appears in the list.

                :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 signalled, 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.

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

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

:ordered-instance-variables 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  which are specially known about by microcode, and 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.

:outside-accessible-instance-variables takes   as   arguments   some   instance
                variables which are to be accessible  from  "outside"  of  this
                object,  that is from functions other than 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
                accessor macros created by defstruct.

                This feature works in two different ways, depending on  whether
                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 will have to be computed at  run  time.    This
                takes  noticeable  time,  although less than actually sending a
                message would take.  An error will be signalled if the argument
                to the accessor macro is not an  instance  or  is  an  instance
                which  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 will compile a call to
                the accessor macro into a subprimitive  which  simply  accesses
                that  variable's assigned slot by number.  This subprimitive is
                only 3 or 4 times slower than car.    The  only  error-checking
                performed  is  to  make  sure  that  the  argument is really an
                instance and is really big enough to contain that slot.   There
                is  no  check  that  the  accessed  slot  really  belongs to an
                instance variable of the appropriate name.  Any functions  that
                use  these  accessor  macros  will have to be recompiled if the
                number or order of instance variables in the flavor is changed.
                The  system  will   not   know   automatically   to   do   this
                recompilation.    If you aren't very careful, you may forget to
                recompile something, and have a very hard-to-find bug.  Because
                of this problem, and because using these macros is less elegant
                than sending messages, the use of this option  is  discouraged.
                In any case the use of these accessor macros should be confined
                to  the  module which owns the flavor, and the "general public"
                should send messages.
:accessor-prefix.      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.    Specifying
                (:accessor-prefix  get$)  would  cause  it  to  be  named get$v
                instead.

:method-combination declares the way that methods from different  flavors  will
                be  combined.    Each "argument" to this option is a list (type
                order message1 message2...).    Message1,  message2,  etc.  are
                names  of  messages  whose  methods  are  to be combined in the
                declared fashion.  type is a keyword which is a defined type of
                combination; see the section on method-combination.  Order is a
                keyword whose interpretation is up to  type;  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  message.    If  no
                component  specifies  a  type  of  method combination, then the
                default type is  used,  namely  :daemon.    If  more  than  one
                component of a flavor specifies it, then they must agree on the
                specification, or else an error is signalled.

:documentation takes  documentation  for  the flavor as arguments.  The list is
                remembered on the flavor's property list as the  :documentation
                property.  The (loose) standard for what can be in this list is
                as  follows;  this  may be extended in the future.  A string is
                documentation on what the flavor is for; this may consist of  a
                brief  overview  in  the first line, then several paragraphs of
                detailed documentation.  A  symbol  is  one  of  the  following
                keywords:

                   - A  :mixin is  a  flavor that you may want to mix with
                     others   to   provide   a   useful   feature.      An
                     :essential-mixin is a flavor that must be mixed in to
                     all  flavors  of its class, or inappropriate behavior
                     will ensue.  A :lowlevel-mixin is a mixin  used  only
                     to   build   other   mixins.    A  :combination is  a
                     combination of flavors for a  specific  purpose.    A
                     :special-purpose flavor  is  a  flavor  used for some
                     internal or kludgey purpose by a particular  program,
                     which is not intended for general use.
11. Flavor Families
  The  following organization conventions are recommended for all programs that
use flavors.
  A base flavor is a flavor that defines a whole family of related flavors, all
of which will have that base flavor as one of their components.  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  messages, :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  are  not
instantiatable and merely 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 would 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  to  do
something,  using that facility's flavors and methods, your program might still
define its own flavors, in a simple way.  The facility  might  provide  a  base
flavor  and  a  set  of  mixins,  and  the  caller can combine these in various
combinations depending on exactly what it wants, since  the  facility  probably
would  not  provide  all  possible  useful  combinations.  Even if your private
flavor has exactly the same components as a pre-existing 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".
12. Vanilla Flavor
  The  messages  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 vanilla-flavor:
vanilla-flavor                                                         [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.

:print-self stream prindepth slashify-p                               [Message]
                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
                prinlevel),  and  whether  slashification  is enabled (prin1 vs
                princ).  Vanilla-flavor ignores the  last  two  arguments,  and
                prints   something  like  #<flavor-name  octal-address>.    The
                flavor-name tells you what  type  of  object  it  is,  and  the
                octal-address  allows  you  to  tell  different  objects  apart
                (provided the garbage collector doesn't move them  behind  your
                back).

:describe                                                             [Message]
                The  object should describe itself, printing a description onto
                the standard-output stream.  The describe function  sends  this
                message   when   it   encounters  an  instance  or  an  entity.
                Vanilla-flavor outputs the object, the name of its flavor,  and
                the names and values of its instance-variables, in a reasonable
                format.

:which-operations                                                     [Message]
                The  object should return a list of the messages it can handle.
                Vanilla-flavor generates the list once per flavor and remembers
                it, minimizing consing and compute-time.  If a  new  method  is
                added,  the  list is regenerated the next time someone asks for
                it.

:operation-handled-p operation                                        [Message]
                operation is a message name.  The object returns whether it has
                a handler for the specified message.

:get-handler-for operation                                            [Message]
                operation is a message name.   The  object  should  return  the
                method  it  uses to handle operation.  If it has no handler for
                that  message,  it  should  return  nil.    This  is  like  the
                get-handler-for  function,  but, of course, you can only use it
                on objects known to accept messages.

:send-if-handles message &rest args                                   [Message]
                The object sends itself message with args if the object handles
                message, otherwise it just returns nil.

:unclaimed-message message &rest args                                 [Message]
                If there is no method for message but there is  a  handler  for
                :unclaimed-message,  the  handler  is  invoked  with  arguments
                message  and  all  of  the   args.      This   is   just   like
                :default-handler, which is an option to defflavor.
13. Method Combination
  Methods  can  have  a  symbolic  type, which is referred to by the particular
method combination defined for that method name.  The  purpose  of  the  method
combination  is  to  produce  a  combined  method  which  will handle the given
message.  It does this by ordering the methods that it finds in some manner and
wrapping invocations of them with various forms.
Basic Method Types

  :primary is the type of method produced when no type is given  to  defmethod.
Therefore, most combinations deal with it.

  :default  by convention takes the place of :primary methods if none exist.  A
base flavor can therefore define default handlers for various operations.

  :wrapper is used internally to remember wrappers.

  :whopper is used internally to remember whoppers.

  :combined is used internally to remember combined methods  (so  they  can  be
inherited).

  Other method types are only used with certain forms of combination.

Predefined Combination Types

  One  specifies  a  method  combination with the :method-combination defflavor
option.  One specifies the name of the  combination  and  possibly  some  other
information, which is usually either :base-flavor-first or :base-flavor-last.

:daemon  :base-flavor-last is the default form of combination.  All the :before
                methods are called, then a single primary method (whose  values
                are  returned  by  the  send),  then all the :after methods are
                called.  The ordering argument to the combination  affects  the
                order of the :before and :after daemons.

:progn
                :and
                :or
                :list
                :append
                :nconc   All   the   primary  methods  are  called  inside  the
                appropriate form.

:daemon-with-or is like :daemon, only the primary method may be  overridden  by
                the  :or  method  types.    The  order  of  the  :or methods is
                controlled by the order  argument  to  the  :method-combination
                defflavor  option.    The  combine dmethod looks something like
                this:

                (progn (foo-before-method)
                       (or (foo-or-method)
                           (foo-primary-method))
                       (foo-after-method))


:daemon-with-and is like :daemon-with-and, only :and methods are wrapper in  an
                and before the primary method.

:daemon-with-override  is  like :daemon-with-or, only the :override methods are
                wrapper in an or before everything else, including  the  before
                and after daemons.

  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 highest level of abstraction is called, then :before  daemons  of
successively  lower  levels  of abstraction 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 lowest level of abstraction is
called, followed by  the  :after  daemons  at  successively  higher  levels  of
abstraction.

  Now, if there is no interaction among all these methods, if their actions are
completely  orthogonal, then it doesn't matter whether you use a :before daemon
or an :after daemon.  It makes 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 message, 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 message 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.

  Of course, since flavors are not  hierarchically  organized,  the  notion  of
levels of abstraction is not strictly applicable.  However, it remains a useful
way of thinking about systems.


Index

          *all-flavor-names*   9
          *dirty-flavors*   9
          *flavor-compile-methods*   9
          *undefined-flavor-names*   9

          :accessor-prefix   12
          :combination   12
          :default-init-plist   9, 11
          :describe   14
          :documentation   12
          :essential-mixin   12
          :get-handler-for   14
          :gettable-instance-variables   6, 11
          :included-flavors   11
          :init-keywords   11
          :initable-instance-variables   6, 11
          :lowlevel-mixin   12
          :method-combination   12
          :mixin   12
          :no-vanilla-flavor   11
          :operation-handled-p   14
          :ordered-instance-variables   11
          :outside-accessible-instance-variables   11
          :print-self   14
          :required-flavors   11
          :required-init-keywords   11
          :required-instance-variables   11
          :required-methods   11
          :send-if-handles   14
          :settable-instance-variables   6, 11
          :special-purpose   12
          :which-operations   14

          Base-flavor   13

          Combined-method   7
          Compile-flavor   10
          Compiler-compile-flavors   10

          Daemon   9
          Daemon methods   7
          Defflavor   6, 9
          Defflavor options   11
          Defmethod   6, 9
          Defwrapper   9, 10

          Flavor   1
          Flavor families   13
          Flavor functions   9
          Flavor-allows-init-keyword-p   10

          Generic operations   4
          Generic operations in lisp   5
          Get-handler-for   10

          Init-plist   6, 9, 11
          Instance   1, 8

          Make-instance   6, 9
          Message   1
          Method   1
          Method combination   15
          Mixin   13
          Mixing flavors   7
          Modularity   3

          Object   1
          Object-oriented programming   2
          Options to defflavor   11

          Recompile-flavor   10

          Self   9
          Set-in-instance   10
          Symeval-in-instance   10

          Undefemethod   9

          Vanilla flavor   14
          Vanilla-flavor   7, 14

          Wrapper   10


                               Table of Contents

1. Introduction                                                               1

2. Objects                                                                    2

3. Modularity                                                                 3

4. Generic Operations                                                         4

5. Generic Operations in Lisp                                                 5

6. Simple Use of Flavors                                                      6

7. Mixing Flavors                                                             7

8. The Instance Datatype                                                      8

9. Flavor Functions                                                           9

10. Defflavor Options                                                        11

11. Flavor Families                                                          13

12. Vanilla Flavor                                                           14

13. Method Combination                                                       15

Index                                                                        16