Page 180

:initform

There are actually two ways to initialize slots in CLOS, initforms and default initargs. The latter are more commonly used.

Here's how default initargs are used when defining circle:

(defclass circle ()
      ((radius :accessor circle-radius :initarg :radius)
      (center :accessor circle-center :initarg :center))
      (:default-initargs
        :radius 1
        :center (cons 0 0)))

What's the difference between default initarg's and initforms? A default initarg is a default value for an initarg. An initform is a default value for a slot.

So what difference does that make? Here's how it works. When you say

(make-instance 'circle ...)

make-instance (in conjunction with initialize-instance) creates an instance of a circle as follows:

So one difference is that default initargs take priority over initforms.

Here's another difference. Suppose we add an area slot to circle:

(defclass circle ()
      ((radius :accessor circle-radius :initarg :radius)
      (center :accessor circle-center :initarg :center)
      (area :accessor circle-area))
      (:default-initargs
        :radius 1
        :center (cons 0 0)))

Note that there is neither an initform nor an initarg for area. Instead, we are going to calculate the area from the radius when the instance is created. Suppose we do this by defining an after method on initialize-instance:

(defmethod initialize-instance :after 
              ((c circle) &key radius &allow-other-keys)
      (setf (circle-area c) (* pi radius radius)))

Now suppose we make the following call:

(make-instance 'circle)

This works fine with our definition of circle. But if we replaced the default initargs with initforms, it would cause an error, because radius would be nil. No :radius argument was given and there was no default value for that argument.

Note that we could define the after method on initialize-instance to work with either class definition as follows:

(defmethod initialize-instance :after 
              ((c circle) &rest args)
      (setf (circle-area c) 
            (* pi (circle-radius c) (circle-radius c))))

This definition has two disadvantages:

For this reason, many programmers use default initargs in their class definitions, rather than initforms.

Page 190

(defclass suit (jacket trousers) ())

Wrong, wrong, wrong! A suit is not a subtype of jacket or trousers. A suit consists of a jacket and trousers.

Don't use "is a" when you mean "has a." This is a classic mistake made by people in all object oriented programming environments.

One reasonable way to do this example is:

(defclass suit ()
      ((jacket :accessor suit-jacket :initarg :jacket)
      (trousers :accessor suit-trousers :initarg :trousers)))

    (defmethod price ((jk jacket)) 350)
    (defmethod price ((tr trousers)) 200)

    (defmethod price ((s suit)) 
      (+ (price (suit-jacket s)) (price (suit-trousers s))))

So, what is a good example requiring non-standard method combinations? Good question. I've not needed them so far.