Skip to content

Class Definition Protocol

Here is a summary diagram of the syntax, procedures and generic functions that may be involved in class definition.

define-class (syntax)

  • class (syntax)

    • make-class (procedure)

      • ensure-metaclass (procedure)

      • make metaclass … (generic)

        • allocate-instance (generic)

        • initialize (generic)

          • compute-cpl (generic)
            • compute-std-cpl (procedure)
          • compute-slots (generic)
          • compute-get-n-set (generic)
          • compute-getter-method (generic)
          • compute-setter-method (generic)
  • class-redefinition (generic)

    • remove-class-accessors (generic)
    • update-direct-method! (generic)
    • update-direct-subclass! (generic)

Wherever a step above is marked as “generic”, it can be customized, and the detail shown below it is only “correct” insofar as it describes what the default method of that generic function does. For example, if you write an initialize method, for some metaclass, that does not call next-method and does not call compute-cpl, then compute-cpl will not be called when a class is defined with that metaclass.

A (define-class ...) form (see Class Definition) expands to an expression which

  • checks that it is being evaluated only at top level
  • defines any accessors that are implied by the slot-definitions
  • uses class to create the new class
  • checks for a previous class definition for name and, if found, handles the redefinition by invoking class-redefinition (see Redefining a Class).

syntax: class name (super …) slot-definition … class-option …

Return a newly created class that inherits from supers, with direct slots defined by slot-definitions and class-options. For the format of slot-definitions and class-options, see define-class.

class expands to an expression which

  • processes the class and slot definition options to check that they are well-formed, to convert the #:init-form option to an #:init-thunk option, to supply a default environment parameter (the current top-level environment) and to evaluate all the bits that need to be evaluated
  • calls make-class to create the class with the processed and evaluated parameters.

procedure: make-class supers slots class-option …

Return a newly created class that inherits from supers, with direct slots defined by slots and class-options. For the format of slots and class-options, see define-class, except note that for make-class, slots is a separate list of slot definitions.

make-class

  • adds <object> to the supers list if supers is empty or if none of the classes in supers have <object> in their class precedence list
  • defaults the #:environment, #:name and #:metaclass options, if they are not specified by options, to the current top-level environment, the unbound value, and (ensure-metaclass supers) respectively
  • checks for duplicate classes in supers and duplicate slot names in slots, and signals an error if there are any duplicates
  • calls make, passing the metaclass as the first parameter and all other parameters as option keywords with values.

procedure: ensure-metaclass supers env

Return a metaclass suitable for a class that inherits from the list of classes in supers. The returned metaclass is the union by inheritance of the metaclasses of the classes in supers.

In the simplest case, where all the supers are straightforward classes with metaclass <class>, the returned metaclass is just <class>.

For a more complex example, suppose that supers contained one class with metaclass <operator-class> and one with metaclass <foreign-object-class>. Then the returned metaclass would be a class that inherits from both <operator-class> and <foreign-object-class>.

If supers is the empty list, ensure-metaclass returns the default GOOPS metaclass <class>.

GOOPS keeps a list of the metaclasses created by ensure-metaclass, so that each required type of metaclass only has to be created once.

The env parameter is ignored.

generic: make metaclass initarg …

metaclass is the metaclass of the class being defined, either taken from the #:metaclass class option or computed by ensure-metaclass. The applied method must create and return the fully initialized class metaobject for the new class definition.

The (make metaclass initarg …) invocation is a particular case of the instance creation protocol covered in the previous section. It will create an class metaobject with metaclass metaclass. By default, this metaobject will be initialized by the initialize method that is specialized for instances of type <class>.

The initialize method for classes (signature (initialize <class> initargs)) calls the following generic functions.

  • compute-cpl class (generic)

    The applied method should compute and return the class precedence list for class as a list of class metaobjects. When compute-cpl is called, the following class metaobject slots have all been initialized: name, direct-supers, direct-slots, direct-subclasses (empty), direct-methods. The value returned by compute-cpl will be stored in the cpl slot.

  • compute-slots class (generic)

    The applied method should compute and return the slots (union of direct and inherited) for class as a list of slot definitions. When compute-slots is called, all the class metaobject slots mentioned for compute-cpl have been initialized, plus the following: cpl, redefined (#f), environment. The value returned by compute-slots will be stored in the slots slot.

  • compute-get-n-set class slot-def (generic)

    initialize calls compute-get-n-set for each slot computed by compute-slots. The applied method should compute and return a pair of closures that, respectively, get and set the value of the specified slot. The get closure should have arity 1 and expect a single argument that is the instance whose slot value is to be retrieved. The set closure should have arity 2 and expect two arguments, where the first argument is the instance whose slot value is to be set and the second argument is the new value for that slot. The closures should be returned in a two element list: (list get set).

    The closures returned by compute-get-n-set are stored as part of the value of the class metaobject’s getters-n-setters slot. Specifically, the value of this slot is a list with the same number of elements as there are slots in the class, and each element looks either like

    emacs-lisp
    (slot-name-symbol init-function . index)

    or like

    emacs-lisp
    (slot-name-symbol init-function get set)

    Where the get and set closures are replaced by index, the slot is an instance slot and index is the slot’s index in the underlying structure: GOOPS knows how to get and set the value of such slots and so does not need specially constructed get and set closures. Otherwise, get and set are the closures returned by compute-get-n-set.

    The structure of the getters-n-setters slot value is important when understanding the next customizable generic functions that initialize calls…

  • compute-getter-method class gns (generic)

    initialize calls compute-getter-method for each of the class’s slots (as determined by compute-slots) that includes a #:getter or #:accessor slot option. gns is the element of the class metaobject’s getters-n-setters slot that specifies how the slot in question is referenced and set, as described above under compute-get-n-set. The applied method should create and return a method that is specialized for instances of type class and uses the get closure to retrieve the slot’s value. initialize uses add-method! to add the returned method to the generic function named by the slot definition’s #:getter or #:accessor option.

  • compute-setter-method class gns (generic)

    compute-setter-method is invoked with the same arguments as compute-getter-method, for each of the class’s slots that includes a #:setter or #:accessor slot option. The applied method should create and return a method that is specialized for instances of type class and uses the set closure to set the slot’s value. initialize then uses add-method! to add the returned method to the generic function named by the slot definition’s #:setter or #:accessor option.