Previous Table of Contents Next


You specialize slot-value-using-class on dependency-class using an around method because you set the return value from calling the method:

   (defmethod slot-value-using-class :around
      ((class dependency-class) object slot)

      (let((name (slot-definition-name slot))
           (result nil))
        ;; Call the primary slot-value method
        (setf result (call-next-method))
        (unless (eq name ‘updates)
          ;; Look for a function fcn associated with name in the
          ;; updates slot of object
          (let ((fcn (getf (part-updates object) name)))

            ;; If we found fcn, run it and use that result
            ;; instead of the result returned by the primary
            ;; slot-value method called above.
            (if fcn (setf result (funcall fcn)))))
       result))

The :around method runs and uses the function getf to look up the function fcn associated with slot-name of the metaobject representing the slot. If this function fcn is present, it is run as an update function and its value is returned. If there is nothing there, you use call-next-method to retrieve the value using the semantics of standard-class. Because the protocol is present for all slots (including the updates slot), you suppress any infinite recursive calls to part-updates by ensuring that the slot being accessed is not the updates slot.

The update function needs to be added to the association list in the updates slot of the object containing the slot to be recalculated on access:

   (defun add-update (object slot-name update-fcn)
      (setf (getf (part-updates object) slot-name)
       update-fcn))

You set up the rule for calculating the weight of a plane:

   (setq w1 (make-instance ‘wing :weight 500))
   (setq b1 (make-instance ‘body :weight 3000))
   (setq e1 (make-instance ‘engine :weight 1000)
   (setq plane (make-instance ‘plane))

   (defun update-weight ()

     (+ (* 2 (weight w1))
           (* 2 (weight e1))

          (weight b1)))
   (add-update plane ‘weight #’update-weight)

(weight plane) returns the value 6000.

The disadvantage of what you have done so far is that the slot values are always being recalculated. As a next step, you add an optimization: Only recalculate slot values only when their dependencies change. The dependency tracking code uses the same version of (setf slot-value-using-class) defined in the previous example.

Whenever the weight slots of w1, b1, or e1 are changed, you want the weight slot of plane to recalculate; otherwise, you cache the value from a prior recalculation in the slot and just return it. Make a change to the slot-value-using-class around method:

   (defmethod slot-value-using-class :around
      ((class dependency-class) object slot)
      (let((name (slot-definition-name slot))
          (result nil))
        (setf result (call-next-method))
        (unless (eq name ‘updates)
          (let ((fcn (getf (part-updates object) name)))
            (if fcn (progn
                    (setf result (funcall fcn))
                    (setf (getf (part-updates object) name)
                              nil)
                    (setf (slot-value object name) result)
                    (setf (getf (part-unused-updates object)
                              name) fcn)))))
         result))

Now on calling (slot-value plane ‘weight), the :around method runs. You check to see if there is an update function for the slot; if there is, you calculate the new value, remove the update function after it has done its work, and stash it in the association list in the unused-updates slot. It needs to be replaced in the association list in the updates slot of plane whenever the weight slots of w1, b1, or e1 are changed again.

This work requires a change to the function that runs whenever (setf slot-value-using-class) is called to change the weight slots of w1, b1, or e1. You implement this work as an anonymous lambda defined in the function add-dependency. The anonymous lambda makes the update function available after changing a slot the weight slot of plane depends on.

Now, when the update function is available, the weight slot of plane is recalculated whenever you access the slot, and if it’s not available there, then you know the slot value is up to date and merely return the value in the slot.

You are still missing a final piece of logic. If a slot in one object depends on a calculated value in a second object, which in turn depends on a value in a third object, changing the value in the third object does not result in a recalculation in the first object (unless the value in the second object has been recalculated after the change). The dependency has not been propagated recursively.


Previous Table of Contents Next