Slot Descriptions

Another aspect of the programmability of is slot-descriptions. This allows the user to control how the slots of a class are accessed. Here we present an example of the use of slot-descriptions to provide a classed (typed) slot facility. The aim is to be able to define a class and, at the same time, the class of the values to be associated with a given slot. The solution is to define a new kind of slot-description to verify that only values of the correct class are stored in the slot. We start by defining a new kind of slot-description <classed-local-slot-description>.

(defclass <classed-local-slot-description> (<local-slot-description>) ((contents-class initform <object> initarg contents-class reader classed-local-slot-description-contents-class)) metaclass <slot-description-class>)

The classed-local-slot-description class inherits the normal slots from <local-slot-description> and adds somewhere to keep track of the allowed class of its contents.

To police the class (type) constraint, we must check that whenever a value is written to a slot with this class—that the value is of the specified kind. we therefore want a new method on compute-primitive-writer-using-slot-description. (defmethod compute-primitive-writer-using-slot-description ((csd <classed-local-slot-description>) cl lst) (let ((std-writer (call-next-method)) (contents-cl (classed-local-slot-description-contents-class csd))) (lambda (obj val) (if (subclassp (class-of val) contents-cl) (std-writer obj val) (error "invalid class of value for slot" some-error 'object obj 'sd csd 'val val)))))

The call to the standard writer is reached only if the value satisfies the class constraint. It just means the value is acceptable—go ahead and do whatever you normally do to put the slot value inside.

All that remains is how to use one of these slots in a class. The example you give can be done as follows—but remember that defclass must be used instead of defstruct because the latter does not support user-defined slot classes.

  (defclass <person> ()
    ((age slot-class <classed-local-slot-description>
          initarg age
          slot-initargs ('contents-class <integer>) accessor age)
     (name slot-class <classed-local-slot-description>
           slot-initargs ('contents-class <string>)
           accessor name) 
     (ordinary-slot initform 'bleagh))
    )

The slots age and name are of the new class of slot with their contents class set to integer and string respectively. Of course, other slots with different classes of slot description may also be defined.

Now, we may type the following:

  (setq i (make <person>)) ((setter age) i 27)

which is fine and (age i) will return 27.

  ((setter age) i 'not-a-number)

but this signals an error. Thanks to Luis Mandel for prompting this example.