[Openmcl-devel] Bug or spec change?

dbm at refined-audiometrics.com dbm at refined-audiometrics.com
Fri Mar 5 09:39:16 PST 2021


Thanks Shanon, 

That was the clearest explanation for the puzzling VALIDATE-SUPERCLASS that I have ever seen!

- DM

> On Mar 4, 2021, at 11:50 PM, Shannon Spires <svs at bearlanding.com> wrote:
> 
> > So it makes no sense to create an instance of COUNTED-CLASS which inherits from RECTANGLE.
> 
> Sure it does. Any given class metaobject has two kinds of relationships with other class metaobjects: It has an INSTANCE-OF relationship with its metaclass and an ISA (or INHERITANCE) relationship with its superclasses.
> 
> The equivalent (and more conventional) definition of counted-rectangle would be
> 
> (defclass counted-rectangle (rectangle)
>   ()
>   (:metaclass counted-class))
> 
> and this makes perfect sense.
> 
> What doesn't work in the example is that rectangle and counted-rectangle have different metaclasses. Any time class A inherits from class B, they both should be instances of the same metaclass. This is the piece of the puzzle that AMOP gets wrong because the book was written before this rule* was established. It's also the reason I dislike using special metaclasses: The semantics of two classes that are instances of different metaclasses inheriting from one another are ... not well-defined. That means every time you use a special metaclass you have to define all your classes in that hierarchy with the :metaclass argument.
> *It's more a convention than a rule. It makes implementors' lives much easier knowing that two inheriting class metaobjects are of the same metaclass. One could fix this problem with a validate-superclass method but whether the resulting class metaobject would actually work is implementation-specific:
> 
> (defmethod validate-superclass ((class counted-class) (superclass standard-class))
>   t)
> 
> That works on Lispworks but not CCL or SBCL, because they're both buggy about how and where they call validate-superclass (see below). This is not the best solution. Here be dragons.
> 
> The best solution which works reliably for all implementations is to redefine rectangle as:
> 
> (defclass rectangle ()
>     ((height :initform 0.0 :initarg :height)
>      (width :initform 0.0 :initarg :width))
>     (:metaclass counted-class))
> 
> But even this is not enough, because there's a subtle bug the way CCL and SBCL call validate-superclass. The above definition of rectangle throws an error:
> > Error: The class #<STANDARD-CLASS STANDARD-OBJECT> was specified as a
> >        super-class of the class #<COUNTED-CLASS RECTANGLE>;
> >        but the meta-classes #<STANDARD-CLASS STANDARD-CLASS> and
> >        #<STANDARD-CLASS COUNTED-CLASS> are incompatible.
> > While executing: #<CCL::STANDARD-KERNEL-METHOD CCL::ENSURE-CLASS-INITIALIZED (CCL::SLOTS-CLASS)> <ccl::STANDARD-KERNEL-METHODCCL::ENSURE-CLASS-INITIALIZED(CCL::SLOTS-CLASS)>, in process Listener(4).
> 
> This error is ridiculous (and a bug) because STANDARD-OBJECT is already a superclass of every CLOS instance (including class metaobjects). This bug occurs in both CCL and SBCL but not Lispworks.
> 
> The fix is to define
> 
> (defmethod ccl::validate-superclass <ccl::validate-superclass> ((c1 standard-class) (c2 standard-object))
>   t)
> 
> [This is not the absolute best way to fix the problem but it works. The real fix is to ensure that CCL and SBCL only call validate-superclass after the class metaobjects in question are initialized enough that they know what they are instances of, and also figure out why this error only happens on special metaclasses. I'll leave that for another day.]
> 
> So a complete solution--using the unorthodox definition style of the AMOP example--is as follows:
> 
> #+CCL
> (defmethod ccl:validate-superclass <ccl:validate-superclass> ((c1 standard-class) (c2 standard-object))
>   "Must define this anytime you're using special metaclasses in CCL."
>   t)
> 
> #+SBCL
> (defmethod sb-mop:validate-superclass ((c1 standard-class) (c2 standard-object))
>   "Must define this anytime you're using special metaclasses in SBCL."
>   t)
> 
> (defclass counted-class (standard-class)
>    ((counter :initform 0)))
> 
> (defclass rectangle ()
>     ((height :initform 0.0 :initarg :height)
>      (width :initform 0.0 :initarg :width))
>     (:metaclass counted-class))
> 
> ; A goofy way to define a class, but it demonstrates dynamic class creation
> (setf (find-class 'counted-rectangle)
>        (make-instance 'counted-class
>          :name 'counted-rectangle
>          :direct-superclasses (list (find-class 'rectangle))
>          :direct-slots ()))
> 
> One more comment: This is a bad example on the part of AMOP but for a different reason than Ron described. If you really want a counted-class, using a class-allocated slot is a much better approach. That way you don't need all this metaclass nonsense.
> 
> -SS
> 
> 
> On 3/4/21 3:31 PM, Ron Garret wrote:
>> The first thing to do is to see if a different CL implementation gives the same error:
>> 
>> ➔ sbcl
>> This is SBCL 1.1.9, an implementation of ANSI Common Lisp.
>> More information about SBCL is available at <http://www.sbcl.org/ <http://www.sbcl.org/>>.
>> 
>> SBCL is free software, provided as is, with absolutely no warranty.
>> It is mostly in the public domain; some portions are provided under
>> BSD-style licenses.  See the CREDITS and COPYING files in the
>> distribution for more information.
>> (defclass rectangle ()
>>    ((height :initform 0.0 :initarg :height)
>>     (width :initform 0.0 :initarg :width)))
>> 
>> #<STANDARD-CLASS RECTANGLE>
>> (defclass counted-class (standard-class)
>>    ((counter :initform 0)))
>> 
>> #<STANDARD-CLASS COUNTED-CLASS>
>> (make-instance 'counted-class
>>          :name 'counted-rectangle
>>          :direct-superclasses (list (find-class 'rectangle))
>>          :direct-slots ())
>> 
>> debugger invoked on a SIMPLE-ERROR:
>>   The class #<STANDARD-CLASS RECTANGLE> was specified as a super-class of the
>>   class #<COUNTED-CLASS COUNTED-RECTANGLE>, but the meta-classes
>>   #<STANDARD-CLASS STANDARD-CLASS> and #<STANDARD-CLASS COUNTED-CLASS> are
>>   incompatible.  Define a method for SB-MOP:VALIDATE-SUPERCLASS to avoid this
>>   error.
>> 
>> So this appears to be a bug in the MOP book.  And, if you think about it, the example in the book really doesn’t make sense.  COUNTED-CLASS is a metaclass, i.e. all of its instances are classes.  But RECTANGLE is not a meta-class, it is a class.  All of its instances are rectangles, which are not classes.  So it makes no sense to create an instance of COUNTED-CLASS which inherits from RECTANGLE.  The instances of such a class would have to be both classes and rectangles, and that’s not possible.
>> 
>> rg
>> 
>> On Mar 4, 2021, at 2:07 PM, Paul Krueger <plkrueger at comcast.net <mailto:plkrueger at comcast.net>> wrote:
>> 
>>> I was trying to do a little MOP hacking and when what I was trying to do got errors I went back to “The Art of the Metaobject Protocol” and ran an example from there to see if it encountered similar errors, which it did (most of this from p. 72 of the book):
>>> 
>>> I’m running Clozure Common Lisp Version 1.11.6 (v1.11.6) DarwinX8664
>>> 
>>> ? (defclass rectangle ()
>>>    ((height :initform 0.0 :initarg :height)
>>>     (width :initform 0.0 :initarg :width)))
>>> #<STANDARD-CLASS RECTANGLE>
>>> ? (defclass counted-class (standard-class)
>>>    ((counter :initform 0)))
>>> #<STANDARD-CLASS COUNTED-CLASS>
>>> ? (setf (find-class 'counted-rectangle)
>>>        (make-instance 'counted-class
>>>          :name 'counted-rectangle
>>>          :direct-superclasses (list (find-class 'rectangle))
>>>          :direct-slots ()))
>>>> Error: The class #<STANDARD-CLASS RECTANGLE> was specified as a
>>>>       super-class of the class #<COUNTED-CLASS COUNTED-RECTANGLE>;
>>>>       but the meta-classes #<STANDARD-CLASS STANDARD-CLASS> and
>>>>       #<STANDARD-CLASS COUNTED-CLASS> are incompatible.
>>>> While executing: #<CCL::STANDARD-KERNEL-METHOD CCL::ENSURE-CLASS-INITIALIZED (CCL::SLOTS-CLASS)> <ccl::STANDARD-KERNEL-METHODCCL::ENSURE-CLASS-INITIALIZED(CCL::SLOTS-CLASS)>, in process Listener(4).
>>>> Type cmd-. to abort, cmd-\ for a list of available restarts.
>>>> Type :? for other options.
>>> 1 > 
>>> ?
>>> 
>>> My question is whether this is a problem with CCL’s implementation or a spec change of some sort that invalidates the example from the book. It’s not clear to me how you could ever employ meta-classes without getting this sort of error in CCL, so if this isn’t a bug, what’s the work-around?
>>> 
>>> Thanks ...
>>> _______________________________________________
>>> Openmcl-devel mailing list
>>> Openmcl-devel at clozure.com <mailto:Openmcl-devel at clozure.com>
>>> https://lists.clozure.com/mailman/listinfo/openmcl-devel <https://lists.clozure.com/mailman/listinfo/openmcl-devel>
>> 
>> 
>> 
>> _______________________________________________
>> Openmcl-devel mailing list
>> Openmcl-devel at clozure.com <mailto:Openmcl-devel at clozure.com>
>> https://lists.clozure.com/mailman/listinfo/openmcl-devel <https://lists.clozure.com/mailman/listinfo/openmcl-devel>
> 
> _______________________________________________
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
> https://lists.clozure.com/mailman/listinfo/openmcl-devel

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.clozure.com/pipermail/openmcl-devel/attachments/20210305/5d0b2bf7/attachment.htm>


More information about the Openmcl-devel mailing list