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

Making (CLASS-OF <class>) be EQ to <class>



Kim Barrett has been using Lucid CLOS, and wanted to model the CLOS 
hierarchy in a parallel fashion; in particular, he needed to make the 
parallel to the circular link at STANDARD-CLASS.  This pointed up three
anomalies to me, and I wonder what your thoughts are about them, and how 
Symbolics CLOS handles those cases.


1.  88-002R defines the actions of a system-supplied method for
    UPDATE-INSTANCE-FOR-DIFFERENT-CLASS, specialized to the point
    STANDARD-OBJECT,STANDARD-OBJECT.  But it doesn't mention the
    special action that obviously must be taken when the object being
    submitted to CHANGE-CLASS is itself a class-object -- namely all
    the instances of that class should be marked obsolete.  Of course,
    the same is true if the class's metaclass has been redefined.  So I 
    would think that there should be system-supplied :BEFORE methods on
    these two updators, specialized to CLASS,CLASS to care for that.


2. So Kim's attempt to do:

     (setq my-std-class (defclass my-std-class (standard-class) ()))
     (change-class my-std-class my-std-class)

   failed because the aforesaid :BEFORE method marks the class as having
   obsolete instances, and the primary method makes the class be a direct
   instance of itself.  Thus the update protocol falls into an infinite
   regress fetching the class of an obsolete instance and needing to update
   that class object first, and so on.   Since this particular example is
   of a class that really doesn't have any instances, then I suggested a
   workaround of just overriding the system-supplied methods at the point 
   CLASS,CLASS and jumping immediately to the "next most specific method."

   But there's a problem here -- you can't say that in CLOS.  At least 
   you can't say it without rolling your own (albeit trivial) method
   delegation coding (I was tempted to say "method-combination coding"
   here, but I really don't think that is quite right).   This seems to
   point out a lacuna in CLOS as compared to flavors.   Didn't flavors
   used to have a thing called SEND-AS?  I know we had to put SEND-AS 
   and LEXPR-SEND-AS in that "EXTEND" object system of MacLisp/NIL back 
   in 1980 or 1981 in order for it to be able to emulate flavors.

   So I just don't see how to do that in CLOS directly; consequently,
   the suggested workaround just unrolls the source-code for the primary 
   method on STANDARD-OBJECT,STANDARD-OBJECT (and of course this would 
   fail if there were other qualified methods on that point).  Note that
   it uses an :AROUND method on CLASS,CLASS, as I do not want to commit
   to whether the intruding piece of behaviour is implemented as a
   :BEFORE method, or as just some line in a primary method.  Here is
   the proffered workaround:

  (setq my-std-class (defclass my-std-class (standard-class) ()))
  (defmethod update-instance-for-different-class :around ((previous class)
							  (current class)
							  &rest initargs)
    (declare (dynamic-extent initargs))
    ;; We'd like to say simply "Run the next most specific effective method"
    ;;  but there's no way to say that in an :around method.  Alternatively,
    ;;  we'd like to say "Dispatch this function again, as if the types
    ;;  of the arguments were one up in their superclass chains," but there's
    ;;  no way to say that either.  So we have just inserted a copy of the
    ;;  source code for the primary method at STANDARD-OBJECT,STANDARD-OBJECT
    ;;  which is only 1 line long anyway.
    (clos::common-update-for-different-class previous current initargs))
  (change-class my-std-class my-std-class)
  (remove-method #'update-instance-for-different-class 
		 (find-method #'update-instance-for-different-class
			      '(:around)
			      (list (find-class 'class) (find-class 'class))))


   I realize that if one knows precisely how the "intruding behaviour" is
   implemented in terms of direct methods, one could just remove those
   methods temporarily, and later restore them.   But the :AROUND method
   approach is so tempting because one would think that is part of the
   purpose of having :AROUND be dominant in the standard method combination 
   technique.  Besides, I really expect the "intruding behaviour" to be
   implemented a different way between in some subsequent release; so it
   would be better not to know whether it comes from a :BEFORE method or
   from a primary method.



3. Shouldn't it be "consequences undefined" if you try to call
   MAKE-INSTANCES-OBSOLETE on STANDARD-CLASS?   In fact, shouldn't it
   be "consequences undefined" to call CHANGE-CLASS on any of the basic 
   metabojects?  

   The cleanup issue LISP-SYMBOL-REDEFINITION (as of mid 1989) doesn't 
   cover these cases of altering system-supplied classes; nor does it 
   seem to cover the case of altering a system-supplied function whose 
   name isn't a symbol -- e.g., (SETF SLOT-VALUE).  To be general, that 
   issue should perhaps have been named LISP-NAME-REDEFINITION, and we 
   should have had "method specs" names for methods, so that the prohibition 
   against redefinition would also apply to things like:

	(METHOD CHANGE-CLASS (STANDARD-OBJECT))			;sure
        (METHOD SHARED-INITIALIZE :AFTER (STANDARD-CLASS)) 	;maybe?

   Possibly these prohibitions are implied by other language in 88-002R,
   but the cleanup issue LISP-SYMBOL-REDEFINITION looks like it is trying
   to be a comprehensive enumeration of what it is you shouldn't redefine.



My apologies if these questions have been addressd in the past, and my
memory just doesn't recall them.



-- JonL --