8.11.6 Customizing Class Definition

If the metaclass of the new class is something more specialized than the default <class>, then the type of class in the calls above is more specialized than <class>, and hence it becomes possible to define generic function methods, specialized for the new class’s metaclass, that can modify or override the default behavior of initialize, compute-cpl or compute-get-n-set.

compute-cpl computes the class precedence list (“CPL”) for the new class (see Class Precedence List), and returns it as a list of class objects. The CPL is important because it defines a superclass ordering that is used, when a generic function is invoked upon an instance of the class, to decide which of the available generic function methods is the most specific. Hence compute-cpl could be customized in order to modify the CPL ordering algorithm for all classes with a special metaclass.

The default CPL algorithm is encapsulated by the compute-std-cpl procedure, which is called by the default compute-cpl method.

procedure: compute-std-cpl class

Compute and return the class precedence list for class according to the algorithm described in Class Precedence List.

compute-slots computes and returns a list of all slot definitions for the new class. By default, this list includes the direct slot definitions from the define-class form, plus the slot definitions that are inherited from the new class’s superclasses. The default compute-slots method uses the CPL computed by compute-cpl to calculate this union of slot definitions, with the rule that slots inherited from superclasses are shadowed by direct slots with the same name. One possible reason for customizing compute-slots would be to implement an alternative resolution strategy for slot name conflicts.

compute-get-n-set computes the low-level closures that will be used to get and set the value of a particular slot, and returns them in a list with two elements.

The closures returned depend on how storage for that slot is allocated. The standard compute-get-n-set method, specialized for classes of type <class>, handles the standard GOOPS values for the #:allocation slot option (see allocation). By defining a new compute-get-n-set method for a more specialized metaclass, it is possible to support new types of slot allocation.

Suppose you wanted to create a large number of instances of some class with a slot that should be shared between some but not all instances of that class - say every 10 instances should share the same slot storage. The following example shows how to implement and use a new type of slot allocation to do this.

(define-class <batched-allocation-metaclass> (<class>))

(let ((batch-allocation-count 0)
      (batch-get-n-set #f))
  (define-method (compute-get-n-set
                     (class <batched-allocation-metaclass>) s)
    (case (slot-definition-allocation s)
      ((#:batched)
       ;; If we've already used the same slot storage for 10 instances,
       ;; reset variables.
       (if (= batch-allocation-count 10)
           (begin
             (set! batch-allocation-count 0)
             (set! batch-get-n-set #f)))
       ;; If we don't have a current pair of get and set closures,
       ;; create one.  make-closure-variable returns a pair of closures
       ;; around a single Scheme variable - see goops.scm for details.
       (or batch-get-n-set
           (set! batch-get-n-set (make-closure-variable)))
       ;; Increment the batch allocation count.
       (set! batch-allocation-count (+ batch-allocation-count 1))
       batch-get-n-set)

      ;; Call next-method to handle standard allocation types.
      (else (next-method)))))

(define-class <class-using-batched-slot> ()
  ...
  (c #:allocation #:batched)
  ...
  #:metaclass <batched-allocation-metaclass>)

The usage of compute-getter-method and compute-setter-method is described in Class Definition Protocol.

compute-cpl and compute-get-n-set are called by the standard initialize method for classes whose metaclass is <class>. But initialize itself can also be modified, by defining an initialize method specialized to the new class’s metaclass. Such a method could complete override the standard behavior, by not calling (next-method) at all, but more typically it would perform additional class initialization steps before and/or after calling (next-method) for the standard behavior.