Node:Metaobjects and the Metaobject Protocol, Next:Terminology, Up:Introductory Remarks
The conceptual building blocks of GOOPS are classes, slot definitions, instances, generic functions and methods. A class is a grouping of inheritance relations and slot definitions. An instance is an object with slots that are allocated following the rules implied by its class's superclasses and slot definitions. A generic function is a collection of methods and rules for determining which of those methods to apply when the generic function is invoked. A method is a procedure and a set of specializers that specify the type of arguments to which the procedure is applicable.
Of these entities, GOOPS represents classes, generic functions and methods as "metaobjects". In other words, the values in a GOOPS program that describe classes, generic functions and methods, are themselves instances (or "objects") of special GOOPS classes that encapsulate the behaviour, respectively, of classes, generic functions, and methods.
(The other two entities are slot definitions and instances. Slot definitions are not strictly instances, but every slot definition is associated with a GOOPS class that specifies the behaviour of the slot as regards accessibility and protection from garbage collection. Instances are of course objects in the usual sense, and there is no benefit from thinking of them as metaobjects.)
The "metaobject protocol" (aka "MOP") is the specification of the generic functions which determine the behaviour of these metaobjects and the circumstances in which these generic functions are invoked.
For a concrete example of what this means, consider how GOOPS calculates
the set of slots for a class that is being defined using
define-class
. The desired set of slots is the union of the new
class's direct slots and the slots of all its superclasses. But
define-class
itself does not perform this calculation. Instead,
there is a method of the initialize
generic function that is
specialized for instances of type <class>
, and it is this method
that performs the slot calculation.
initialize
is a generic function which GOOPS calls whenever a new
instance is created, immediately after allocating memory for a new
instance, in order to initialize the new instance's slots. The sequence
of steps is as follows.
define-class
uses make
to make a new instance of the
<class>
, passing as initialization arguments the superclasses,
slot definitions and class options that were specified in the
define-class
form.
make
allocates memory for the new instance, and then invokes the
initialize
generic function to initialize the new instance's
slots.
initialize
generic function applies the method that is
specialized for instances of type <class>
, and this method
performs the slot calculation.
In other words, rather than being hardcoded in define-class
, the
behaviour of class definition is encapsulated by generic function
methods that are specialized for the class <class>
.
It is possible to create a new class that inherits from <class>
,
which is called a "metaclass", and to write a new initialize
method that is specialized for instances of the new metaclass. Then, if
the define-class
form includes a #:metaclass
class option
whose value is the new metaclass, the class that is defined by the
define-class
form will be an instance of the new metaclass rather
than of the default <class>
, and will be defined in accordance
with the new initialize
method. Thus the default slot
calculation, as well as any other aspect of the new class's relationship
with its superclasses, can be modified or overridden.
In a similar way, the behaviour of generic functions can be modified or
overridden by creating a new class that inherits from the standard
generic function class <generic>
, writing appropriate methods
that are specialized to the new class, and creating new generic
functions that are instances of the new class.
The same is true for method metaobjects. And the same basic mechanism
allows the application class author to write an initialize
method
that is specialized to their application class, to initialize instances
of that class.
Such is the power of the MOP. Note that initialize
is just one
of a large number of generic functions that can be customized to modify
the behaviour of application objects and classes and of GOOPS itself.
Each subsequent section of the reference manual covers a particular area
of GOOPS functionality, and describes the generic functions that are
relevant for customization of that area.
We conclude this subsection by emphasizing a point that may seem
obvious, but contrasts with the corresponding situation in some other
MOP implementations, such as CLOS. The point is simply that an
identifier which represents a GOOPS class or generic function is a
variable with a first-class value, the value being an instance of class
<class>
or <generic>
. (In CLOS, on the other hand, a
class identifier is a symbol that indexes the corresponding class
metaobject in a separate namespace for classes.) This is, of course,
simply an extension of the tendency in Scheme to avoid the unnecessary
use of, on the one hand, syntactic forms that require unevaluated
arguments and, on the other, separate identifier namespaces (e.g. for
class names), but it is worth noting that GOOPS conforms fully to this
Schemely principle.