[Openmcl-devel] Bug or spec change?

Shannon Spires svs at bearlanding.com
Thu Mar 4 22:50:49 PST 2021


 > 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)>, 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 ((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 ((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)>, 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
>
>
> _______________________________________________
> 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/20210304/ebc4fb18/attachment.htm>


More information about the Openmcl-devel mailing list