[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Making (CLASS-OF <class>) be EQ to <class>
- To: moon@symbolics.com
- Subject: Making (CLASS-OF <class>) be EQ to <class>
- From: Jon L White <jonl@lucid.com>
- Date: Mon, 24 Sep 90 20:38:43 PDT
- Cc: common-lisp-object-system@mcc.com
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 --