29.2. Overview

29.2.1. Metaobjects
29.2.1.1. Classes
29.2.1.2. Slot Definitions
29.2.1.3. Generic Functions
29.2.1.4. Methods
29.2.1.5. Specializers
29.2.1.6. Method Combinations
29.2.2. Inheritance Structure of Metaobject Classes
29.2.2.1. Implementation and User Specialization
29.2.2.1.1. Restrictions on Portable Programs
29.2.2.1.2. Restrictions on Implementations
29.2.3. Processing of the User Interface Macros
29.2.3.1. Compile-file Processing of the User Interface Macros
29.2.3.2. Compile-file Processing of Specific User Interface Macros
29.2.4. Metaobject Initialization Protocol

29.2.1. Metaobjects

For each kind of program element there is a corresponding basic metaobject class . These are the classes: CLASS, CLOS:SLOT-DEFINITION, GENERIC-FUNCTION, METHOD and METHOD-COMBINATION. A metaobject class is a subclass of exactly one of these classes. The results are undefined if an attempt is made to define a CLASS that is a subclass of more than one basic metaobject class. A metaobject is an instance of a metaobject class.

Each metaobject represents one program element. Associated with each metaobject is the information required to serve its role. This includes information that might be provided directly in a user interface macro such as DEFCLASS or DEFMETHOD. It also includes information computed indirectly from other metaobjects such as that computed from class inheritance or the full set of methods associated with a generic function.

Much of the information associated with a metaobject is in the form of connections to other metaobjects. This interconnection means that the role of a metaobject is always based on that of other metaobjects. As an introduction to this interconnected structure, this section presents a partial enumeration of the kinds of information associated with each kind of metaobject. More detailed information is presented later.

29.2.1.1. Classes

A class metaobject determines the structure and the default behavior of its instances. The following information is associated with class metaobjects:

  • The name, if there is one, is available as an object.
  • The direct subclasses, direct superclasses and class precedence list are available as lists of class metaobjects.
  • The slots defined directly in the class are available as a list of direct slot definition metaobjects. The slots which are accessible in instances of the class are available as a list of effective slot definition metaobjects.
  • The methods which use the class as a specializer, and the generic functions associated with those methods are available as lists of method and generic function metaobjects respectively.
  • The documentation is available as a STRING or NIL.

See also Section 29.3, “Classes”

29.2.1.2. Slot Definitions

A slot definition metaobject contains information about the definition of a slot. There are two kinds of slot definition metaobjects:

direct slot definition metaobject
Used to represent the direct definition of a slot in a class. This corresponds roughly to the slot specifiers found in DEFCLASS forms.
effective slot definition metaobject
Used to represent information, including inherited information, about a slot which is accessible in instances of a particular class.

Associated with each class metaobject is a list of direct slot definition metaobjects representing the slots defined directly in the class. Also associated with each class metaobject is a list of effective slot definition metaobjects representing the set of slots accessible in instances of that class.

The following information is associated with both direct and effective slot definitions metaobjects:

  • The name, allocation, and type are available as forms that could appear in a DEFCLASS form.
  • The initialization form, if there is one, is available as a form that could appear in a DEFCLASS form. The initialization form together with its lexical environment is available as a function of no arguments which, when called, returns the result of evaluating the initialization form in its lexical environment. This is called the initfunction of the slot.
  • The slot filling initialization arguments are available as a list of symbols.
  • The documentation is available as a STRING or NIL.

Certain other information is only associated with direct slot definition metaobjects. This information applies only to the direct definition of the slot in the class (it is not inherited).

  • The function names of those generic functions for which there are automatically generated reader and writer methods. This information is available as lists of function names. Any accessors specified in the DEFCLASS form are broken down into their equivalent readers and writers in the direct slot definition.

Information, including inherited information, which applies to the definition of a slot in a particular class in which it is accessible is associated only with effective slot definition metaobjects.

  • For certain slots, the location of the slot in instances of the class is available.

See also Section 29.4, “Slot Definitions”

29.2.1.3. Generic Functions

A generic function metaobject contains information about a generic function over and above the information associated with each of the generic function's methods.

  • The name is available as a function name.
  • The methods associated with the generic function are available as a list of method metaobjects.
  • The default class for this generic function's method metaobjects is available as a class metaobject.
  • The lambda list is available as a LIST.
  • The method combination is available as a method combination metaobject.
  • The argument precedence order is available as a permutation of those symbols from the lambda list which name the required arguments of the generic function.
  • The declarations are available as a list of declaration specifiers.

    Note

    There is a slight misnomer in the naming of functions and options in this document: Where the term declaration is used, actually a declaration specifier is meant.

  • The documentation is available as a STRING or NIL.

See also Section 29.5, “Generic Functions”

29.2.1.4. Methods

A method metaobject contains information about a specific METHOD.

  • The qualifiers are available as a LIST of of non-NIL atoms.
  • The lambda list is available as a LIST.
  • The specializers are available as a list of specializer metaobjects.
  • The function is available as a FUNCTION. This function can be applied to arguments and a list of next methods using APPLY or FUNCALL.
  • When the method is associated with a generic function, that generic function metaobject is available. A method can be associated with at most one generic function at a time.
  • The documentation is available as a STRING or NIL.

See also Section 29.6, “Methods”

29.2.1.5. Specializers

A specializer metaobject represents the specializers of a METHOD. class metaobjects are themselves specializer metaobjects. A special kind of specializer metaobject is used for EQL specializers.

See also Section 29.8, “Specializers”

29.2.1.6. Method Combinations

A method combination metaobject represents the information about the method combination being used by a generic function.

Note

This document does not specify the structure of method combination metaobjects.

See also Section 29.9, “Method Combinations”

29.2.2. Inheritance Structure of Metaobject Classes

Figure 29.1. Inheritance structure of metaobject classes

Inheritance structure of metaobject classes

The inheritance structure of the specified metaobject classes is shown in Table 29.1, “Direct Superclass Relationships Among The Specified Metaobject Classes”. The class of every class shown is STANDARD-CLASS except for the classes T and FUNCTION, which are instances of the class BUILT-IN-CLASS, and the classes GENERIC-FUNCTION and STANDARD-GENERIC-FUNCTION, which are instances of the class CLOS:FUNCALLABLE-STANDARD-CLASS.


Each class with a yes in the Abstract column is an abstract class and is not intended to be instantiated. The results are undefined if an attempt is made to make an instance of one of these classes with MAKE-INSTANCE.

Each class with a yes in the Subclassable column can be used as direct superclass for portable programs. It is not meaningful to subclass a class that has a no in this column.

Implementation dependent: only in CLISP

The class METHOD is also subclassable: It is possible to create subclasses of METHOD that do not inherit from STANDARD-METHOD.

Implementation dependent: only in CLISP and some other implementations

The class CLOS:FUNCALLABLE-STANDARD-OBJECT's class precedence list contains FUNCTION before STANDARD-OBJECT, not after STANDARD-OBJECT. This is the most transparent way to realize the [ANSI CL standard] requirement (see the [ANSI CL standard] section 4.2.2 Type Relationships) that GENERIC-FUNCTION's class precedence list contains FUNCTION before STANDARD-OBJECT.

The classes STANDARD-CLASS, CLOS:STANDARD-DIRECT-SLOT-DEFINITION, CLOS:STANDARD-EFFECTIVE-SLOT-DEFINITION, STANDARD-METHOD, CLOS:STANDARD-READER-METHOD, CLOS:STANDARD-WRITER-METHOD and STANDARD-GENERIC-FUNCTION are called standard metaobject classes. For each kind of metaobject, this is the class the user interface macros presented in the CLOS use by default. These are also the classes on which user specializations are normally based.

The classes BUILT-IN-CLASS, CLOS:FUNCALLABLE-STANDARD-CLASS and CLOS:FORWARD-REFERENCED-CLASS are special-purpose class metaobject classes. Built-in classes are instances of the class BUILT-IN-CLASS. The class CLOS:FUNCALLABLE-STANDARD-CLASS provides a special kind of instances described in Section 29.10.2, “Funcallable Instances”. When the definition of a class references another class which has not yet been defined, an instance of CLOS:FORWARD-REFERENCED-CLASS is used as a stand-in until the class is actually defined.

Implementation of class CLOS:FORWARD-REFERENCED-CLASS in CLISP

The class CLOS:FORWARD-REFERENCED-CLASS is implemented in a way that fixes several flaws in the [AMOP] specification.

It is not a subclass of CLASS and CLOS:SPECIALIZER, just a subclass of CLOS:METAOBJECT, because forward references to classes are not classes and cannot be used as specializers of methods. An [AMOP] compatibility mode is provided, however, if you set the variable CUSTOM:*FORWARD-REFERENCED-CLASS-MISDESIGN* to T. In this mode, CLOS:FORWARD-REFERENCED-CLASS is formally a subclass of CLASS and CLOS:SPECIALIZER, but the behaviour of CLOS:FORWARD-REFERENCED-CLASS instances is the same.

The [AMOP] says that the first argument of CLOS:ENSURE-CLASS-USING-CLASS can be a CLOS:FORWARD-REFERENCED-CLASS. But from the description of CLOS:ENSURE-CLASS, it is clear that it can only be a class returned by FIND-CLASS, and [ANSI CL standard] FIND-CLASS cannot return a CLOS:FORWARD-REFERENCED-CLASS.

The [AMOP] says that CLOS:ENSURE-CLASS-USING-CLASS creates a CLOS:FORWARD-REFERENCED-CLASS for not-yet-defined class symbols among the direct-superclasses list. But this leads to many CLOS:FORWARD-REFERENCED-CLASS with the same name (since they cannot be stored and retrieved through FIND-CLASS), and since CHANGE-CLASS preserves the EQ-ness, after the class is defined, we have many class objects with the same name.

In the direct-superclasses list of non-finalized classes, CLOS:FORWARD-REFERENCED-CLASS instances can occur, denoting classes that have not yet been defined. When or after such a class gets defined, the CLOS:FORWARD-REFERENCED-CLASS instance is replaced with the real class. CLISP uses simple object replacement, not CHANGE-CLASS, in this process.

The class STANDARD-OBJECT is the default direct superclass of the class STANDARD-CLASS. When an instance of the class STANDARD-CLASS is created, and no direct superclasses are explicitly specified, it defaults to the class STANDARD-OBJECT. In this way, any behavior associated with the class STANDARD-OBJECT will be inherited, directly or indirectly, by all instances of the class STANDARD-CLASS. A subclass of STANDARD-CLASS may have a different class as its default direct superclass, but that class must be a subclass of the class STANDARD-OBJECT.

The same is true for CLOS:FUNCALLABLE-STANDARD-CLASS and CLOS:FUNCALLABLE-STANDARD-OBJECT.

The class CLOS:SPECIALIZER captures only the most basic behavior of method specializers, and is not itself intended to be instantiated. The class CLASS is a direct subclass of CLOS:SPECIALIZER reflecting the property that classes by themselves can be used as method specializers. The class CLOS:EQL-SPECIALIZER is used for EQL specializers.

29.2.2.1. Implementation and User Specialization

The purpose of the Metaobject Protocol is to provide users with a powerful mechanism for extending and customizing the basic behavior of the CLOS. As an object-oriented description of the basic CLOS behavior, the Metaobject Protocol makes it possible to create these extensions by defining specialized subclasses of existing metaobject classes.

The Metaobject Protocol provides this capability without interfering with the implementor's ability to develop high-performance implementations. This balance between user extensibility and implementor freedom is mediated by placing explicit restrictions on each. Some of these restrictions are general---they apply to the entire class graph and the applicability of all methods. These are presented in this section.

The following additional terminology is used to present these restrictions:

  • Metaobjects are divided into three categories. Those defined in this document are called specified ; those defined by an implementation but not mentioned in this document are called implementation-specific ; and those defined by a portable program are called portable .
  • A class i is interposed between two other classes k1 and k2 if and only if there is some path, following direct superclasses, from the class k1 to the class k2 which includes i.
  • A method is specialized to a class if and only if that class is in the list of specializers associated with the method; and the method is in the list of methods associated with some generic function.
  • In a given implementation, a specified method is said to have been promoted if and only if the specializers of the method, x1 ... xn, are defined in this specification as the classes k1 ... kn, but in the implementation, one or more of the specializers xl, is a superclass of the class given in the specification kl.
  • For a given generic function and set of arguments, a method k2 extends a method k1 if and only if:

    1. k1 and k2 are both associated with the given generic function
    2. k1 and k2 are both applicable to the given arguments,
    3. the specializers and qualifiers of the methods are such that when the generic function is called, k2 is executed before k1,
    4. k1 will be executed if and only if CALL-NEXT-METHOD is invoked from within the body of k2 and
    5. CALL-NEXT-METHOD is invoked from within the body of k2, thereby causing k1 to be executed.
  • For a given generic function and set of arguments, a method k2 overrides a method k1 if and only if conditions i through iv above hold and, instead of v,

    1. CALL-NEXT-METHOD is not invoked from within the body of k2, thereby preventing k1 from being executed.
29.2.2.1.1. Restrictions on Portable Programs

Portable programs are allowed to define subclasses of specified classes, and are permitted to define methods on specified generic functions, with the following restrictions:

  • Portable programs must not redefine any specified classes, generic functions, methods or method combinations. Any method defined by a portable program on a specified generic function must have at least one specializer that is neither a specified class nor an EQL specializer whose associated value is an instance of a specified class.
  • Portable programs may define methods that extend specified methods unless the description of the specified method explicitly prohibits this. Unless there is a specific statement to the contrary, these extending methods must return whatever value was returned by the call to CALL-NEXT-METHOD.
  • Portable programs may define methods that override specified methods only when the description of the specified method explicitly allows this. Typically, when a method is allowed to be overridden, a small number of related methods will need to be overridden as well.

    An example of this is the specified methods on the generic functions CLOS:ADD-DEPENDENT, CLOS:REMOVE-DEPENDENT and CLOS:MAP-DEPENDENTS. Overriding a specified method on one of these generic functions requires that the corresponding method on the other two generic functions be overridden as well.

  • Portable methods on specified generic functions specialized to portable metaobject classes must be defined before any instances of those classes (or any subclasses) are created, either directly or indirectly by a call to MAKE-INSTANCE. Methods can be defined after instances are created by ALLOCATE-INSTANCE however. Portable metaobject classes cannot be redefined.

    Note

    The purpose of this last restriction is to permit implementations to provide performance optimizations by analyzing, at the time the first instance of a metaobject class is initialized, what portable methods will be applicable to it. This can make it possible to optimize calls to those specified generic functions which would have no applicable portable methods.

    Implementation dependent: only in CLISP

    When a metaobject class is redefined, CLISP issues a WARNING that the redefinition has no effect. To avoid this warning, place all metaobject class definitions in a separate file, compile it in a separate session (because DEFCLASS in CLISP is evaluated at compile time too; see Section 29.2.3.2, “Compile-file Processing of Specific User Interface Macros”), and then LOAD it only once per session.

The results are undefined if any of these restrictions are violated.

Note

The specification technology used in this document needs further development. The concepts of object-oriented protocols and subclass specialization are intuitively familiar to programmers of object-oriented systems; the protocols presented here fit quite naturally into this framework. Nonetheless, in preparing this document, we have found it difficult to give specification-quality descriptions of the protocols in a way that makes it clear what extensions users can and cannot write. Object-oriented protocol specification is inherently about specifying leeway, and this seems difficult using current technology.

29.2.2.1.2. Restrictions on Implementations

Implementations are allowed latitude to modify the structure of specified classes and methods. This includes: the interposition of implementation-specific classes; the promotion of specified methods; and the consolidation of two or more specified methods into a single method specialized to interposed classes.

Any such modifications are permitted only so long as for any portable class k that is a subclass of one or more specified classes k1 ... kn, the following conditions are met:

  • In the actual class precedence list of k, the classes k1 ... kn must appear in the same order as they would have if no implementation-specific modifications had been made.
  • The method applicability of any specified generic function must be the same in terms of behavior as it would have been had no implementation-specific changes been made. This includes specified generic functions that have had portable methods added. In this context, the expression the same in terms of behavior means that methods with the same behavior as those specified are applicable, and in the same order.
  • No portable class k may inherit, by virtue of being a direct or indirect subclass of a specified class, any slot for which the name is a symbol accessible in the COMMON-LISP-USER package or exported by any package defined in the [ANSI CL standard].
  • Implementations are free to define implementation-specific before- and after-methods on specified generic functions. Implementations are also free to define implementation-specific around-methods with extending behavior.

29.2.3. Processing of the User Interface Macros

A list in which the first element is one of the symbols DEFCLASS, DEFMETHOD, DEFGENERIC, DEFINE-METHOD-COMBINATION, CLOS:GENERIC-FUNCTION, CLOS:GENERIC-FLET or CLOS:GENERIC-LABELS, and which has proper syntax for that macro is called a user interface macro form. This document provides an extended specification of the DEFCLASS, DEFMETHOD and DEFGENERIC macros.

The user interface macros DEFCLASS, DEFGENERIC and DEFMETHOD can be used not only to define metaobjects that are instances of the corresponding standard metaobject class, but also to define metaobjects that are instances of appropriate portable metaobject classes. To make it possible for portable metaobject classes to properly process the information appearing in the macro form, this document provides a limited specification of the processing of these macro forms.

User interface macro forms can be evaluated or compiled and later executed. The effect of evaluating or executing a user interface macro form is specified in terms of calls to specified functions and generic functions which provide the actual behavior of the macro. The arguments received by these functions and generic functions are derived in a specified way from the macro form.

Converting a user interface macro form into the arguments to the appropriate functions and generic functions has two major aspects: the conversion of the macro argument syntax into a form more suitable for later processing, and the processing of macro arguments which are forms to be evaluated (including method bodies).

In the syntax of the DEFCLASS macro, the initform and default-initarg-initial-value-form arguments are forms which will be evaluated one or more times after the macro form is evaluated or executed. Special processing must be done on these arguments to ensure that the lexical scope of the forms is captured properly. This is done by building a function of zero arguments which, when called, returns the result of evaluating the form in the proper lexical environment.

In the syntax of the DEFMETHOD macro the forms argument is a list of forms that comprise the body of the method definition. This list of forms must be processed specially to capture the lexical scope of the macro form. In addition, the lexical functions available only in the body of methods must be introduced. To allow this and any other special processing (such as slot access optimization), a specializable protocol is used for processing the body of methods. This is discussed in Section 29.6.3.1.1, “Processing Method Bodies”.

29.2.3.1. Compile-file Processing of the User Interface Macros

It is a common practice for Common Lisp compilers, while processing a file or set of files, to maintain information about the definitions that have been compiled so far. Among other things, this makes it possible to ensure that a global macro definition (DEFMACRO form) which appears in a file will affect uses of the macro later in that file. This information about the state of the compilation is called the COMPILE-FILE environment.

When compiling files containing CLOS definitions, it is useful to maintain certain additional information in the COMPILE-FILE environment. This can make it possible to issue various kinds of warnings (e.g., lambda list congruence) and to do various performance optimizations that would not otherwise be possible.

At this time, there is such significant variance in the way existing Common Lisp implementations handle COMPILE-FILE environments that it would be premature to specify this mechanism. Consequently, this document specifies only the behavior of evaluating or executing user interface macro forms. What functions and generic functions are called during COMPILE-FILE processing of a user interface macro form is not specified. Implementations are free to define and document their own behavior. Users may need to check implementation-specific behavior before attempting to compile certain portable programs.

29.2.3.2. Compile-file Processing of Specific User Interface Macros

DEFCLASS

Section 29.3.1, “Macro DEFCLASS

Implementation dependent: only in CLISP

CLISP evaluates DEFCLASS forms also at compile time.

DEFMETHOD

Section 29.6.3.1, “Macro DEFMETHOD

Implementation dependent: only in CLISP

CLISP does not evaluate DEFMETHOD forms at compile time except as necessary for signature checking.

DEFGENERIC

Section 29.5.3.1, “Macro DEFGENERIC

Implementation dependent: only in CLISP

CLISP does not evaluate DEFGENERIC forms at compile time except as necessary for signature checking.

29.2.4. Metaobject Initialization Protocol

Like other objects, metaobjects can be created by calling MAKE-INSTANCE. The initialization arguments passed to MAKE-INSTANCE are used to initialize the metaobject in the usual way. The set of legal initialization arguments, and their interpretation, depends on the kind of metaobject being created. Implementations and portable programs are free to extend the set of legal initialization arguments. Detailed information about the initialization of each kind of metaobject are provided in the appropriate sections:


These notes document CLISP version 2.49.93+Last modified: 2018-02-19