Previous Table of Contents Next


5.3.6. Standard Method Combination

CLOS permits generic functions to be split into several interacting components. A primary method performs the main work of the computation. All the methods defined so far in the examples are primary methods. The primary method can be augmented by auxiliary methods: before methods, after methods, and around methods. For example, suppose you are plotting points. The primary method for plotting the point might have the following implementation:

   (defmethod draw ((p point) window)
      (move-to window (x-coordinate p) (y-coordinate p))
      (draw-point window))

As part of this protocol, you might want to notify another part of the system that fits a line to a set of already plotted points. We would like to add a specialization that encapsulates this particular behavior without interfering with the primary draw method. We can do this by adding an after method, which does the notification after the point is plotted:

   (defmethod draw :after ((p point window))
      (fit-new-line (add-to-point-set p) window))

Using auxiliary methods is convenient. It is typically used to establish initial conditions (as a before method) and cleanup conditions (as an after method). They are also commonly used to add triggers (or notifications) to operations, as in the example given here. Building systems such as blackboards, where components interact solely by posting results to a common area (called a blackboard) and where the other components read results off it, use triggers to make sure the component doing the writing notifies other components that the results are available for reading.

CLOS supports multiple before methods and multiple after methods. When the generic function is called, all before methods are run, then the most applicable primary method, followed by all the after methods. This pattern of calling is known as standard method combination, although CLOS supports other combinations described in section 5.4.7. Standard method combination has other features, described in section 5.4.6, where the detailed rules for standard method combination are also given.

5.3.7. The Metaobject Protocol

One way CLOS is different from any other object-oriented language today is that CLOS supports an object-oriented protocol (the MOP) for customizing the actual object protocol implemented for standard CLOS. Using the MOP permits programmers to extend CLOS by overriding the standard CLOS semantics I have been describing. You can add persistent classes, new protocols for accessing slots, or custom behavior for methods.

In C++, classes specify the layout of an object in memory and specify the names used in the source code to manipulate components of the object. Once the code is compiled for execution, the class information is gone. In CLOS and in Smalltalk, the compiled code contains an object that retains the class information at runtime. This object is the runtime representation of the class definition in the source code and is known as an instance. In Smalltalk (as in Java), the layout of the object representing a class is fixed, corresponding to the semantics of the object system. In Common Lisp, however, the object system can represent objects in multiple ways and has the ability to add more by customization via the MOP.

Common Lisp contains more than CLOS classes; it has a pure procedural component with built-in, non-extensible, data types (known as built-in class) such as streams, and also a simple object system (known as structures and defined by defstruct). Because there is more than one type of class, the system must not only have a runtime representation of the classes, but it must also keep track of each type of class. The type of a class is known as the metaclass and is represented as an object available at runtime. The system uses the information in a class object’s metaclass to decide how to handle it. Regular CLOS classes are instances of standard-class.

Similarly to classes, other elements of CLOS programs such as slots, generic functions, methods, and method combination all have their metaclasses. In the MOP, this set of metaclasses is called metaobject classes. Having all these objects exist in the runtime makes CLOS programs runtime extensible. New classes can be defined at any time (using the class metaobjects or metaclasses), new methods can be added (using the method metaobjects), and so on.

For example, standard CLOS classes specify slots that are implemented as a set of named locations in an instance laid out in the memory of the computer. This is a highly efficient implementation along the lines of most object-oriented languages. Calling slot-value retrieves the value by looking in this memory location and returning the value. However, there are other semantics. You can subclass standard-class to customize slot-value to do something different. For example, you might decide that setting the value in memory should trigger custom behavior (such as checking security access privileges). You might decide that the value isn’t really in memory at all; calling slot-value instead returns the result of a calculation even though it is convenient in the program source code to think about the attribute as if it were there in memory. For example, the value might depend upon accessing a database. I’ll give some examples of this type in section 5.4.9.1 because customizing slot-value turns out to be a powerful programming paradigm.


Previous Table of Contents Next