Previous | Table of Contents | Next |
The MOP is subtle and discussing it is difficult because it is easy to get mixed up between the objects in the typical object-oriented programs and the metaobjects used in CLOS. However, consider the following:
In the metaobject programming,
Its worth adding a note on efficiency. It seems that the MOP might add a tremendous amount of overhead into the object system. However, implementations of Common Lisp normally implement a large number of optimizations specific for the semantics of standard-class. If you start using the MOP to build custom metaclasses, then some of the optimizations applied to standard-class are overridden by the custom behavior. In some cases (e.g., in the case of lazy evaluation and dependency tracking discussed in section 5.4.9.1), the custom behavior is more efficient for the type of problem being solved. In all cases clarity, maintainability, and extensibility of the source code are dramatically improved.
This section gives some of the more detailed rules for CLOS programming and delves into areas where CLOS is somewhat different from most other object-oriented languages.
There are two default types of slots: Slots may be local, in which case they are of type instance, or they may be of type class, in which case they are shared by all instances of the class.
To give an example, Ill redefine the class square:
(defclass square (convex-region) ((number-of-sides :initform 4 :allocation :class) (lower-left :initform (make-point :x 0 :y 0) :initarg :lower-left :accessor lower-left) (upper-right:initform (make-point :x 1 y 1) :initarg :upper-right :accessor upper-right)))
number-of-sides is declared as a class-allocated slot. All instances share this slot. A diagram of the situation is
The implementation of accessors is always done by using slot-value. slot-value accesses the slot directly, without calling the accessor methods. As discussed previously, exposing this as part of the interface makes it easy to break the interface when the internal representation changes. In standard CLOS programming, user code only uses slot-value for a couple reasons: defining custom accessors and debugging.
5.4.2.1. Using with-accessors and with-slots
CLOS provides two convenient macros for accessing slots in code bodies by treating the slots as variable names: with-accessors and with-slots.
We discuss with-accessors first. In code composing the body of a with-accessors macro, using a variable has exactly the same effect as calling the corresponding accessor function:
(defgeneric area (figure) ;; returns the area of a figure )
is an interface defined to calculate the area of a figure. You can define the area method on a square as follows:
(defmethod area ((square square)) (with-accessors ((l lower-left) (u upper-right)) square (let ((xlow (x-coordinate l) (xhigh x-coordinate u)) (ylow (y-coordinate l)) (yhigh (y-coordinate u))) (* (- xhigh xlow) (- yhigh ylow))))
is exactly equivalent to
(defmethod area ((square square)) (let ((xlow (x-coordinate (lower-left square)) (xhigh x-coordinate (upper-right square)) (ylow (y-coordinate (lower-left square)) (yhigh (y-coordinate (upper-right square))) (* (- xhigh xlow) (- yhigh ylow))))
As another example,
(defgeneric grow-figure (figure amount) ;; grows the area of a figure by a relative amount )
is an interface to return a new figure with its area increased by amount. The implementation for a method specialized on a square using with-accessors is
(defmethod grow-figure ((square square)) (with-accessors ((l lower-left) (u upper-right)) square (let ((xlow (x-coordinate l) (xhigh (x-coordinate u)) (ylow (y-coordinate l)) (yhigh (y-coordinate u)) (adj (sqrt amount))) (setf u (make-point :x (+ xlow (* (- xhigh xlow) adj)) :y (+ ylow (* (- yhigh ylow) adj)) )))))
Inside with-accessors, the form
(setf u expression)
is exactly equivalent to
(setf (upper-right square) expression)
The macro with-slots is exactly analogous to with-accessors. The following:
(defmethod distance ((p point)) (with-slots (x y) p (sqrt (+ (* x x) (* y y)))))
is equivalent to this:
(defmethod distance ((p point)) (sqrt (+ (* (slot-value p x) (slot-value p x)) (* (slot-value p y) (slot-value p y)))))
Previous | Table of Contents | Next |