[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

specializer metaobjects



In the current metaobject protocol is that EQL specializers are not
objects (with their own special class).  Aside from being conceptually
gross, this leads to a number of problems.  There are several methods
that, in a silly way, are specialized to CONS.  It also means that the
MOP doesn't point the way for users who want to add a new kind of
specializer.  It doesn't make it clear that they should add a new kind
of object for the new specializers rather than, for example, further
overloading CONS.

This message is a proposal to try and fix this by making EQL
specializers themselves be metaobjects.  The general idea is to define a
new kind of metaobject, called <specializers>.  Classes are
specializers.  There is a new class SPECIALIZER, of which CLASS is a
subclass.  Another new class EQL-SPECIALIZER, is a direct subclass of
SPECIALIZER.

In the past I have avoided doing this because I believed that 88-002R
specifically said that the form of eql specializers (not specializer
names) was a list of which the car was the symbol EQL etc.  I know
believe that 88-002R doesn't say that because it doesn't have the
conceptual framework required to do so.

The proposal presented here solves the problems outlined above and
moreover it paves the way for adding a new lower layer to the method
lookup protocol that would make it easier for users adding new kinds of
specializers.

The tricky point is in describing the conversion of the external format
of specializers (what appears in DEFMETHOD forms) to specializer
metaobjects.  This conversion needs to be based on a notion of
`interning'.  That is two external forms which both describe the `same'
specializer should in fact produce the same specializer metaobject.  The
definition of `same' depends on the specializer in question.  For
classes, the interning function is just FIND-CLASS.  For EQL
specializers, the interning predicate is EQL. And so on.

Here is some naive model implementation code:

(defclass class (specializer) ..)

(defun intern-class-specializer (class-name)
  (find-class class-name))


(defclass eql-specializer (specializer)
     ((object :initarg :object
              :reader eql-specializer-object)))

(defvar *eql-specializers* (make-hash-table :test #'eql))

(defun intern-eql-specializer (object)
  (or (gethash object *eql-specializers*)
      (setf (gethash object *eql-specializers*)
            (make-instance 'eql-specializer :object object))))

So, a defmethod form and expansion would be something like:

  (defmethod foo ((x foo) (y (eql bar))) ..)

  (...
     (make-instance 'standard-method
       :specializers (list (intern-class-specializer 'foo)
                           (intern-eql-specializer bar))
       .
       .)
   ...)

Note that the intern-xxx functions will have to be specified in order to
allow users to do anonymous creation of methods with EQL specializers.

If the user wants to do something like the slot class specializers
discussed at the CLOS workshop they would do:

(defclass slot-class-specializer (specializer)
     ((slot-name :initarg :slot-name ..)
      (class     :initarg :class     ..)))

(defvar *slot-class-specializers* (make-hash-table :test #'equal))

(defun intern-slot-class-specializer (slot-name class-name)
  (let ((class (find-class class-name)))
    (or (gethash (cons slot-name class) *slot-class-specializers*)
        (setf (gethash (cons slot-name class) *slot-class-specializers*)
              (make-instance 'slot-class-specializer 
                             :slot-name slot-name
                             :class class)))))
                                                

and a method definition and expansion would look like:

  (define-method foo ((x foo)
                      (y (eql bar))
                      (z (slot a baz)))  ;means that the value of the
                                         ;slot named A of this argument
                                         ;should have class BAZ
    ..)

  (...
     (make-instance 'my-method
       :specializers  (list (intern-class-specializer 'foo)
                            (intern-eql-specializer bar)
                            (intern-slot-class-specializer 'a 'baz))
       .
       .)
   ...)