From: Joel Ray Holveck
Subject: Metaclass
Date: 
Message-ID: <y7cpubz3164.fsf@sindri.juniper.net>
I've never really had call to experiment with metaclasses before, but
found where they'd make my life easier tonight.  But my first
experiment ended with me scratching my head.  I'm using CMUCL 18c (PCL
from 16Sep92).  This stuff is coming from AMOP (except the definition
of RECTANGLE is trivially different), so I was a bit suprised when PCL
didn't like it, even though it is coming from the first part where
he's building up Closette.

(defclass counted-class (standard-class)
  ((counter :initform 0)))
(defmethod make-instance :after ((class counted-class) &key)
  (incf (slot-value class 'counter)))
(defclass rectangle ()
  ((width :initarg :width :initform 10)
   (height :initarg :height :initform 10)))
(defclass counted-rectangle (rectangle)
  ()
  (:metaclass counted-class))

(Taken from AMOP pp 72, 73, 15, and 76, respectively.)  Now, the way I
understand, the idea here is to keep track of how many
COUNTED-RECTANGLE instances are floating around.  The problem is, when
I eval that last form, I get the error:

 Error in function PCL::|(FAST-METHOD SHARED-INITIALIZE :AFTER (STD-CLASS T))|:
    The class #<Standard-Class RECTANGLE {48211155}> was specified as a
 
			 super-class of the class #<Counted-Class COUNTED-RECTANGLE {4802DE7D}>;
 but the meta-classes #<Standard-Class PCL::STANDARD-CLASS {28140C85}> and
 #<Standard-Class COUNTED-CLASS {482236FD}> are incompatible.
 Define a method for PCL:VALIDATE-SUPERCLASS to avoid this error.

Now, I looked at the documentation for VALIDATE-SUPERCLASS, and sure
enough it's doing its job.  The authors also remark: "Defining a
method on validate-superclass requires detailed knowledge of of [sic]
the internal protocol followed by each of the two class metaobject
classes." (p 241)  I didn't see any other mention of
VALIDATE-SUPERCLASS in the index, and although it sounds familiar, I
couldn't find a discussion in the body.

In this particular instance, COUNTED-RECTANGLE and RECTANGLE have two
different metaclasses.  But if I had not derived COUNTED-RECTANGLE
from RECTANGLE, it still would have been implicitly derived from
STANDARD-OBJECT and I get the same error.  (That was my problem on my
first attempt.)

So, is a user expected to define a method for VALIDATE-SUPERCLASS for
any metaclass they create (with all the "detailed knowledge" that AMOP
says that requires), regardless of how it's to be used, or am I
missing something?

Thanks,
joelh

From: Tim Bradshaw
Subject: Re: Metaclass
Date: 
Message-ID: <nkjlmmne649.fsf@tfeb.org>
Joel Ray Holveck <·····@juniper.net> writes:

> 
> In this particular instance, COUNTED-RECTANGLE and RECTANGLE have two
> different metaclasses.  But if I had not derived COUNTED-RECTANGLE
> from RECTANGLE, it still would have been implicitly derived from
> STANDARD-OBJECT and I get the same error.  (That was my problem on my
> first attempt.)
> 
> So, is a user expected to define a method for VALIDATE-SUPERCLASS for
> any metaclass they create (with all the "detailed knowledge" that AMOP
> says that requires), regardless of how it's to be used, or am I
> missing something?
> 

yes, you have to define methods.  The `detailed knowledge' think is
slightly misleading: what you are saying is that it is OK for this to
be a subclass of that even though their metaclasses are different.  In
particular you're stating that the protocols that the metaclasses
require are compatible in some sense.  Since you've just defined the
protocol for your COUNTED-CLASS class, you have full knowledge of it,
and you know that, in fact, it is compatible, so things are OK.

Note that there is a subtlety in that if you say:

(defmethod validate-superclass ((class counted-class) (superclass standard-class))
  t)

You are saying that *any subclass* of counted-class is valid for *any
subclass* of standard-class (barring more specific methods), and this
is possibly too liberal.  The predefined method on (class class) does
this thing with checking that the two classes are the same to avoid
this issue.

Note also that some implementations (I forget which) have some extra
methods on VALIDATE-SUPERCLASS which mean that you don't actually need
to define these methods.  I got caught by that when moving code to a
less liberal implementation, especially as AMOP almost fails to
mention that you need to write these methods (I think it does not give
them in any of the examples), so I hadn't.

--tim
From: Joel Ray Holveck
Subject: Re: Metaclass
Date: 
Message-ID: <y7c7ky67tdc.fsf@sindri.juniper.net>
>> In this particular instance, COUNTED-RECTANGLE and RECTANGLE have two
>> different metaclasses.  But if I had not derived COUNTED-RECTANGLE
>> from RECTANGLE, it still would have been implicitly derived from
>> STANDARD-OBJECT and I get the same error.  (That was my problem on my
>> first attempt.)
>> So, is a user expected to define a method for VALIDATE-SUPERCLASS for
>> any metaclass they create (with all the "detailed knowledge" that AMOP
>> says that requires), regardless of how it's to be used, or am I
>> missing something?
> yes, you have to define methods.  The `detailed knowledge' think is
> slightly misleading: what you are saying is that it is OK for this to
> be a subclass of that even though their metaclasses are different.  In
> particular you're stating that the protocols that the metaclasses
> require are compatible in some sense.  Since you've just defined the
> protocol for your COUNTED-CLASS class, you have full knowledge of it,
> and you know that, in fact, it is compatible, so things are OK.

Okay.  Let me ask another question.  Suppose I had wanted to make
RECTANGLE a COUNTED-CLASS.  Is it the Right Thing for RECTANGLE to
still derive from STANDARD-OBJECT, or is there another way I should go
about that, too?

Thanks,
joelh
From: Tim Bradshaw
Subject: Re: Metaclass
Date: 
Message-ID: <nkj7ky5lpfu.fsf@tfeb.org>
Joel Ray Holveck <·····@juniper.net> writes:

> 
> Okay.  Let me ask another question.  Suppose I had wanted to make
> RECTANGLE a COUNTED-CLASS.  Is it the Right Thing for RECTANGLE to
> still derive from STANDARD-OBJECT, or is there another way I should go
> about that, too?
> 

No, I think it's fine.  At least it is what I do!

--tim
From: Joel Ray Holveck
Subject: Re: Metaclass
Date: 
Message-ID: <y7c4rta7rwe.fsf@sindri.juniper.net>
> Note that there is a subtlety in that if you say:
> (defmethod validate-superclass ((class counted-class) (superclass standard-class))
>   t)
> You are saying that *any subclass* of counted-class is valid for *any
> subclass* of standard-class (barring more specific methods), and this
> is possibly too liberal.  The predefined method on (class class) does
> this thing with checking that the two classes are the same to avoid
> this issue.

You you're saying that something like this would be more appropriate:

(defmethod pcl:validate-superclass ((class counted-class)
				    (superclass pcl::standard-class))
  (or (call-next-method)
      (and (eq (type-of class) 'counted-class)
	   (eq (type-of superclass) 'pcl::standard-class))))

(I was quite confused writing this until I discovered that
standard-class is internal to PCL.)

Thanks,
joelh
From: Pierre R. Mai
Subject: Re: Metaclass
Date: 
Message-ID: <87n172nnn0.fsf@orion.bln.pmsf.de>
Joel Ray Holveck <·····@juniper.net> writes:

> (defmethod pcl:validate-superclass ((class counted-class)
> 				    (superclass pcl::standard-class))
>   (or (call-next-method)
>       (and (eq (type-of class) 'counted-class)
> 	   (eq (type-of superclass) 'pcl::standard-class))))

I'd use (pcl::)class-of instead of type-of here, and I'd wouldn't call
the next method.  If someone has knowledge about descendants, he'll
write his own method, which will override your method.

> 
> (I was quite confused writing this until I discovered that
> standard-class is internal to PCL.)

Yes, this is one of the hand-ful of idiosyncrasies of PCL as included
in CMU CL at this moment in time (pending further work on integrating
PCL more deeply into CMU CL), i.e. PCL::STANDARD-CLASS and
CL:STANDARD-CLASS are different, and only the former works for MOP
work.  For some other pitfalls you might want to take a look at the
EncyCMUCLopedia, where some articles deal with these items.

Regs, Pierre.

-- 
Pierre R. Mai <····@acm.org>                    http://www.pmsf.de/pmai/
 The most likely way for the world to be destroyed, most experts agree,
 is by accident. That's where we come in; we're computer professionals.
 We cause accidents.                           -- Nathaniel Borenstein
From: Joel Ray Holveck
Subject: Re: Metaclass
Date: 
Message-ID: <y7c3d8t7bv7.fsf@sindri.juniper.net>
>> (defmethod pcl:validate-superclass ((class counted-class)
>> 				    (superclass pcl::standard-class))
>>   (or (call-next-method)
>>       (and (eq (type-of class) 'counted-class)
>> 	   (eq (type-of superclass) 'pcl::standard-class))))
> I'd use (pcl::)class-of instead of type-of here,

Ah, good call.

> and I'd wouldn't call the next method.  If someone has knowledge
> about descendants, he'll write his own method, which will override
> your method.

I was doing that to handle the cases that ((class class) (superclass
class)) handles, such as if (eql class superclass).  Is that not
necessary, then?

>> (I was quite confused writing this until I discovered that
>>  standard-class is internal to PCL.)
> Yes, this is one of the hand-ful of idiosyncrasies of PCL as included
> in CMU CL at this moment in time (pending further work on integrating
> PCL more deeply into CMU CL), i.e. PCL::STANDARD-CLASS and
> CL:STANDARD-CLASS are different, and only the former works for MOP
> work.  For some other pitfalls you might want to take a look at the
> EncyCMUCLopedia, where some articles deal with these items.

Excellent, I forgot to check that out.  Thanks!

So this isn't a general problem with other implementations that use
PCL?  (I only have one other Lisp implementation handy, and it uses a
different CLOS implementation, but I want to avoid problems porting
this to other systems.)

>  The most likely way for the world to be destroyed, most experts agree,
>  is by accident. That's where we come in; we're computer professionals.
>  We cause accidents.                           -- Nathaniel Borenstein

I love this .sig, BTW.

joelh
From: Pierre R. Mai
Subject: Re: Metaclass
Date: 
Message-ID: <87g0ct1kcm.fsf@orion.bln.pmsf.de>
Joel Ray Holveck <·····@juniper.net> writes:

> > and I'd wouldn't call the next method.  If someone has knowledge
> > about descendants, he'll write his own method, which will override
> > your method.
> 
> I was doing that to handle the cases that ((class class) (superclass
> class)) handles, such as if (eql class superclass).  Is that not
> necessary, then?

Yes and no.  It is only necessary for the case of two counted-class
instances (classes), since your method will also match those.  I'd
probably write an explicit check for that situation, i.e.

(defmethod pcl:validate-superclass ((class counted-class)
 				    (superclass counted-class))
  (and (eq (pcl::class-of class) (pcl::find-class 'counted-class))
       (member (pcl::class-of superclass) 
               (mapcar #'pcl::find-class 
                       '(counted-class pcl::standard-class)))))

If you wanted to stay with the call-next-method approach, I'd put the
call-next-method at the end of the or expression (the situation seems
more "fall-through-like" to me).

> So this isn't a general problem with other implementations that use
> PCL?  (I only have one other Lisp implementation handy, and it uses a
> different CLOS implementation, but I want to avoid problems porting
> this to other systems.)

I don't know for sure, but the WRAPPER-CLASS stuff seemed CMU CL
specific the last time I looked.

> >  The most likely way for the world to be destroyed, most experts agree,
> >  is by accident. That's where we come in; we're computer professionals.
> >  We cause accidents.                           -- Nathaniel Borenstein
> 
> I love this .sig, BTW.

I think I quoted that directly from Borenstein's "Programming as if
People Mattered: Friendly Programs, Software Engineering, and Other
Noble Delusions", which I'd recommend to any serious programmer.

Regs, Pierre.

-- 
Pierre R. Mai <····@acm.org>                    http://www.pmsf.de/pmai/
 The most likely way for the world to be destroyed, most experts agree,
 is by accident. That's where we come in; we're computer professionals.
 We cause accidents.                           -- Nathaniel Borenstein
From: Tim Bradshaw
Subject: Re: Metaclass
Date: 
Message-ID: <nkj66dplpcp.fsf@tfeb.org>
Joel Ray Holveck <·····@juniper.net> writes:

> 
> You you're saying that something like this would be more appropriate:
> 
> (defmethod pcl:validate-superclass ((class counted-class)
> 				    (superclass pcl::standard-class))
>   (or (call-next-method)
>       (and (eq (type-of class) 'counted-class)
> 	   (eq (type-of superclass) 'pcl::standard-class))))
> 

Yes, that's what I mean.  The method defined by AMOP does something
like this (I think it returns T if the two classes are the same).

> (I was quite confused writing this until I discovered that
> standard-class is internal to PCL.)
> 

yes, that's a pain in CMUCL.

--tim