Previous Table of Contents Next


Note that make-instance for square now takes new initargs. This interface can’t be preserved. The convention is to define a constructor function that must be redefined to call make-instance with the new arguments, as in

   (defun make-square (lower-left upper-right)
      (make-instance ‘square :xlow (x-coordinate lower-left)
                           :ylow (y-coordinate lower-left)
                           :xhigh (x-coordinate upper-right)
                           :yhigh (y-coordinate upper-right)))

which would replace the original constructor function:

    (defun make-square (lower-left upper-right)
       (make-instance ‘square :lower-left lower-left
                            :upper-right upper-right))

5.4.8.2. Changing the Class of an Instance

When an instance is changed to a new class, the slots have the following update behavior:

  Shared slots in the original class are untouched, but the instance cannot access them any longer.
  Slots in the original class keep their values if they are local in the new class. If they were unbound, they remain unbound. If they are shared in the new class, they are changed to refer to value of the shared slot in the new class.
  New slots from the new class are added to the instance and are initialized according to the new class’s slot initialization protocol.
  Slots only present in the original class are discarded.

As in updating an instance for a redefined class, instances can be updated by defining a method for update-instance-for-redefined-class. This generic function takes as arguments a copy of the instance before it was updated and the updated instance. Because the original class still exists unchanged, the copy of the instance before it was updated can be accessed using slot-value or any of the accessors defined in the original class.

As in updating an instance for a redefined class, this is normally done by using before methods or after methods.

5.4.9. Extending CLOS Using the Metaobject Protocol

This section shows some techniques for extending CLOS. These use the MOP and de facto extensions for CLOS described in Part II of The Art of the Metaobject Protocol (Kizcales, Rivieres, Bobrow, 1991), implemented by most Common Lisp systems. For an excellent description of how a MOP is implemented, see The Art of the Metaobject Protocol (Kizcales, Rivieres, Bobrow, 1991). Note that this reference is a simplified form of the MOP for teaching purposes.

Every ordinary CLOS class is an instance of a system-supplied class metaobject class (or metaclass). This particular metaclass is called standard-class and, like all other CLOS objects, it has slots, values, and methods. When you made the class cookies-n-cream, an object was made that completely describes the cookies-n-cream class. The slots and values in this object are determined by standard-class. The following shows what the metaclass object might look like:

Slot Value

Name cookies-n-cream
Direct superclasses (vanilla oreos edible standard-object t)
Direct slots ()
Effective slots fat-content
Direct subclasses ()
Class standard-object

5.4.9.1. Very Large Spreadsheets and Dependency Tracking

This section outlines two techniques often used in CAD applications.

As an example, Boeing designs very large planes using classes and instances to model the aircraft. Designers at Boeing iterate over a plane design by changing properties of objects in the model. It is often convenient to model the object properties as slot values of the object. However, many properties are often dependent on the slot values in other objects. For example, the seating capacity of the plane may be calculated from the height and width and length of the passenger compartment, but the length of the plane may be dependent on other dimensions of components of the plane.

Designing a plane is like manipulating a very large spreadsheet, where changing the value in a cell necessitates an update to other cell values that are calculated using the changed value. As in a spreadsheet, you want the system to automatically keep track of all the formulas defining the slot values of objects and their dependencies on other values.

Two cases have different semantics for tracking and updating such dependencies. In one case, when you change a value, you need to know all the dependent values immediately. For example, if a plane is being drawn on the screen, then changing a property should immediately result in all the dependent properties being updated correctly so that the view on the screen remains consistent.

Immediate update becomes impractical when the models are large. While working on one aspect of the model (say the seating arrangement), you may not need other parts of the model. In this case, to improve performance, it is better to defer updating the other parts of the model until they are needed.

We’ll discuss the first case first.

Dependency Tracking Applied to Immediate Update

First of all, use the following code so that the functions for the metaobject protocols are visible in the current package:

   (use-package :clos)

Now define a new type of metaclass by subclassing standard-class. All the classes in the model use the semantics of this metaclass:

   (defclass dependency-class (standard-class) ())

At this point, you can make new classes of this type:

    (defclass part ()
       ((dependency :initform nil
                :accessor part-dependency))
        (:metaclass dependency-class))
    (defclass body (part)
       ((length :reader lngth)
        (width :initform 0 :accessor width))
       (:metaclass dependency-class))
    (defclass wing (part)
       ((length :initform 0 :accessor lngth))
        (:metaclass dependency-class))


Previous Table of Contents Next