Previous Table of Contents Next


5.3.4. Multiple Argument Dispatch

CLOS is different from most other object-oriented languages because generic functions can dispatch on more than one argument. In this way, the interfaces defined by generic functions do not necessarily belong to just one class. This is quite unlike both Smalltalk and C++,1 where methods are specific to one class.


1C++ does implement a form of multiple argument dispatch, but only for built-in C++ operators, where it is known as operator overloading. It is not available for user-defined methods.

For example, you might want to write methods for draw that are implemented differently for different displays. For example, the following defines different subclasses of display types:

   (defclass display ())
   (defclass postscript-printer (display))
   (defclass microsoft-window (display))

Now the specialized method for draw on circle and postscript-printer would consist of

   (defmethod draw ((figure circle) (display postscript-printer))
      (code to generate postscript for drawing a circle))

and for drawing to microsoft-window:

   (defmethod draw ((figure circle) (display microsoft-window))
      (code to draw the circle on a Microsoft window))

In this case, draw specializes on both arguments: the figure to be drawn and the display to draw it on.

5.3.5. Structure Encapsulation and Slots

A CLOS class consists of slots. Slots can always be accessed through a call to slot-value. This is not the recommended convention for accessing slots. Access options are provided as keyword options in the definition of the class itself, and you’ve seen these without explanations in prior examples.

In the example of point, the published method for getting the value of the x slot of a point p1 is

   (x-coordinate p1)

which returns the value of the x slot.

In CLOS, the syntax for setting a slot uses the Common Lisp macro setf. Setting the value of the x slot of p1 is

   (setf (x-coordinate p1) 33)

which sets the x slot of p1 to 33.

The generic functions for x-coordinate (setting and getting) are automatically generated by the keyword :accessor in the following code:

   (defclass point ()
      ((x :accessor x-coordinate)
       (y :accessor y-coordinate)))

The accessor functions are defined in terms of the function slot-value, which takes the object and the name of the slot as arguments. It defines x-coordinate as

   (defmethod x-coordinate ((p point))
      (slot-value p ‘x))

Note here that most Common Lisp systems optimize this definition, so this definition may be transformed into an optimization for slot value lookup.

Similarly, when the slot value is modified using the published setf function

   (setf (x-coordinate p1) 33)

the system converts this in an internal function that is called to actually set the value. Common Lisp uses the notation (setf x-coordinate) for the actual function that sets the value, and this composite name is known as a function specification. The following code

   (setf (x-coordinate p1) 33)

sets the value using the following definition:

   (defmethod (setf x-coordinate) (new-value (object point))
      (setf (slot-value object ‘x) new-value))

Once again, an actual implementation would normally optimize this definition. It’s important to note that this method dispatches on the second argument, not on the first. CLOS generic function syntax, as described in the previous section, permits any combination of dispatching on the arguments.

CLOS supports publishing a :reader, which generates only the function to retrieve slot-value, :writer, which generates only the function to modify slot-value, and :accessor, (described previously), which generates both the :reader method and the :writer method.

Unlike C++, where member functions are only visible in the lexical scope of the class, so that member functions in derived classes do not have access to member data elements in base classes, CLOS permits access to slots by name at all times and in any context. In CLOS, the convention of defining accessors is just that: a convention. However, it is strongly recommended that CLOS programmers use this convention; otherwise, it is too easy to break encapsulation and introduce unwanted dependencies on implementation details. C++ solves the problem of making names available using the protected and public specifiers, but usual C++ conventions recommend making public access only available through user-defined virtual functions (for the same reason as CLOS programmers are recommended to use defined accessors).

By contrast, Smalltalk enforces public access to instance variables only through user-defined methods. Instance variable names are visible only to methods defined in the same class (or in a subclass) that the instance variable is defined in.

By providing the mechanisms for accessors, CLOS encourages publishing access to slot values using interfaces defined by the accessor names but does not enforce it. This is unlike Smalltalk, or, to a lesser extent, C++.


Previous Table of Contents Next