From: Maciej Katafiasz
Subject: MOP problems -- a fairly basic case
Date: 
Message-ID: <fhgvv5$ahj$1@news.net.uni-c.dk>
Hi,

I've been fighting with MOP today, and there's obviously something I'm
missing. What I want to do is a simple metaclass for serialisable packets
that would ensure proper type definitions so that packets can be declared
and guaranteed to have a defined specialisatiuon.

I have the following code:

(in-package :pp)
 
(defclass serialisable (standard-class)
  ())
 
(defmethod make-instance :after ((class serialisable) &rest args)
  (format t "Packet: ~A" args))
 
(defmethod sb-mop:validate-superclass ((class serialisable)
	   			      (superclass standard-class))
  t)
 
(defmacro defpacket (&body body)
  `(defclass ,@body
       (:metaclass serialisable)))
 
PP> (defpacket fasd ()
      ((foo :initarg :foo)))
 
===>
#<POKER-PACKET::SERIALISABLE FASD> ;; Expected Packet: ...
 
(defmethod make-instance :after ((class standard-class) &rest args)
  (format t "Packet: ~A" args))
 
PP> (pp::defpacket fasd ()
      ((foo :initarg :foo)))
===>
Packet: (CLASS #<SERIALISABLE FASD> NAME FOO READERS NIL WRITERS NIL
         INITARGS (FOO))
#<POKER-PACKET::SERIALISABLE FASD>

I don't understand the above. If I define a class X with metaclass Y, then
there should happen a call to (make-instance 'Y). Which is exactly what my
first defmethod attempts to do, except it doesn't work. Even more baffling
is how it does get invoked when I specialise on STANDARD-CLASS, and
clearly shows there *is* SERIALISABLE involved. I'd be very grateful for
any explanation of this mystery, I'm sure it's something simple and silly,
but I'm unable to track it down.

Cheers,
Maciej

From: Pascal Costanza
Subject: Re: MOP problems -- a fairly basic case
Date: 
Message-ID: <5q2fqkFu1md2U1@mid.individual.net>
Maciej Katafiasz wrote:
> Hi,
> 
> I've been fighting with MOP today, and there's obviously something I'm
> missing. What I want to do is a simple metaclass for serialisable packets
> that would ensure proper type definitions so that packets can be declared
> and guaranteed to have a defined specialisatiuon.
> 
> I have the following code:
> 
> (in-package :pp)
>  
> (defclass serialisable (standard-class)
>   ())
>  
> (defmethod make-instance :after ((class serialisable) &rest args)
>   (format t "Packet: ~A" args))
>  
> (defmethod sb-mop:validate-superclass ((class serialisable)
> 	   			      (superclass standard-class))
>   t)
>  
> (defmacro defpacket (&body body)
>   `(defclass ,@body
>        (:metaclass serialisable)))
>  
> PP> (defpacket fasd ()
>       ((foo :initarg :foo)))
>  
> ===>
> #<POKER-PACKET::SERIALISABLE FASD> ;; Expected Packet: ...
>  
> (defmethod make-instance :after ((class standard-class) &rest args)
>   (format t "Packet: ~A" args))
>  
> PP> (pp::defpacket fasd ()
>       ((foo :initarg :foo)))
> ===>
> Packet: (CLASS #<SERIALISABLE FASD> NAME FOO READERS NIL WRITERS NIL
>          INITARGS (FOO))
> #<POKER-PACKET::SERIALISABLE FASD>
> 
> I don't understand the above. If I define a class X with metaclass Y, then
> there should happen a call to (make-instance 'Y). Which is exactly what my
> first defmethod attempts to do, except it doesn't work. Even more baffling
> is how it does get invoked when I specialise on STANDARD-CLASS, and
> clearly shows there *is* SERIALISABLE involved. I'd be very grateful for
> any explanation of this mystery, I'm sure it's something simple and silly,
> but I'm unable to track it down.

Indeed, looks like a bug to me. Which CL implementation are you using?

As a workaround, specialize on initialize-instance instead of 
make-instance...



Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Maciej Katafiasz
Subject: Re: MOP problems -- a fairly basic case
Date: 
Message-ID: <fhh3o5$b04$1@news.net.uni-c.dk>
Den Thu, 15 Nov 2007 09:54:43 +0100 skrev Pascal Costanza:

>> I don't understand the above. If I define a class X with metaclass Y,
>> then there should happen a call to (make-instance 'Y). Which is exactly
>> what my first defmethod attempts to do, except it doesn't work. Even
>> more baffling is how it does get invoked when I specialise on
>> STANDARD-CLASS, and clearly shows there *is* SERIALISABLE involved. I'd
>> be very grateful for any explanation of this mystery, I'm sure it's
>> something simple and silly, but I'm unable to track it down.
> 
> Indeed, looks like a bug to me. Which CL implementation are you using?

SBCL 1.0.10

> As a workaround, specialize on initialize-instance instead of
> make-instance...

Will try that, thanks. Come to think of it, does it look like something 
Closer MOP would fix, or is it a genuine bug you can only get fixed in 
the implementation?

Cheers,
Maciej
From: Pascal Costanza
Subject: Re: MOP problems -- a fairly basic case
Date: 
Message-ID: <5q2mcjFtbdnuU1@mid.individual.net>
Maciej Katafiasz wrote:
> Den Thu, 15 Nov 2007 09:54:43 +0100 skrev Pascal Costanza:
> 
>>> I don't understand the above. If I define a class X with metaclass Y,
>>> then there should happen a call to (make-instance 'Y). Which is exactly
>>> what my first defmethod attempts to do, except it doesn't work. Even
>>> more baffling is how it does get invoked when I specialise on
>>> STANDARD-CLASS, and clearly shows there *is* SERIALISABLE involved. I'd
>>> be very grateful for any explanation of this mystery, I'm sure it's
>>> something simple and silly, but I'm unable to track it down.
>> Indeed, looks like a bug to me. Which CL implementation are you using?

Actually, it's not a bug, but the correct behavior.

Assume the following metaclass definition:

(defclass my-metaclass (standard-class) ())

This:

(defclass foo ... (:metaclass my-metaclass))

results in a call to this:

(ensure-class 'foo ... :metaclass my-metaclass ...)

which results, among other things, in an invocation of 
ensure-class-using-class and ultimately this:

(make-instance 'my-metaclass ...)

According to the specification of make-instance this means:

(make-instance (find-class 'my-metaclass) ...)

The class my-metaclass itself is an instance of standard-class (there 
was no :metaclass option in the definition of my-metaclass to the 
contrary), so methods on make-instance specialized on my-metaclass won't 
trigger.

>> As a workaround, specialize on initialize-instance instead of
>> make-instance...
> 
> Will try that, thanks.

According to the specification of make-instance (Section 7.1.7 of the 
HyperSpec), you can see that make-instance and allocate-instance are 
invoked on the class, but initialize-instance is invoked on the fresh 
instance. So initialize-instance is the proper generic function to 
specialize on.

You could also do this:

(defmethod make-instance :after
   ((class (eql (find-class 'my-metaclass))) &rest initargs)
   ...)

However, this has two problems:

a) You don't get the subclasses of my-metaclass in this way.

b) You'd be violating the restrictions stated in Section 11.1.2.1.2 of 
the HyperSpec. (Although I think the restriction #19 is too strict in 
the case of eql specializers.)

> Come to think of it, does it look like something 
> Closer MOP would fix, or is it a genuine bug you can only get fixed in 
> the implementation?

It ain't broken, so I won't fix it. ;-)


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Maciej Katafiasz
Subject: Re: MOP problems -- a fairly basic case
Date: 
Message-ID: <fhhi05$b04$2@news.net.uni-c.dk>
Den Thu, 15 Nov 2007 11:46:42 +0100 skrev Pascal Costanza:

> Assume the following metaclass definition:
> 
> (defclass my-metaclass (standard-class) ())
> 
> This:
> 
> (defclass foo ... (:metaclass my-metaclass))
> 
> results in a call to this:
> 
> (ensure-class 'foo ... :metaclass my-metaclass ...)
> 
> which results, among other things, in an invocation of
> ensure-class-using-class and ultimately this:
> 
> (make-instance 'my-metaclass ...)
> 
> According to the specification of make-instance this means:
> 
> (make-instance (find-class 'my-metaclass) ...)
> 
> The class my-metaclass itself is an instance of standard-class (there
> was no :metaclass option in the definition of my-metaclass to the
> contrary), so methods on make-instance specialized on my-metaclass won't
> trigger.

However, foo is an object of class my-metaclass. Therefore its creation 
should be done by the means of make-instance, shouldn't it?

Although actually there doesn't seem to be specified any protocol that 
would spell out what steps are _required_ for conforming creation of an 
instance, at least I'm unable to find it. There are specified in CLHS 
access points that _allow_ one to create instances, but it's not said 
which ones _will_ be invoked on all creation. Or am I reading it wrong?

>>> As a workaround, specialize on initialize-instance instead of
>>> make-instance...
>> 
>> Will try that, thanks.
> 
> According to the specification of make-instance (Section 7.1.7 of the
> HyperSpec), you can see that make-instance and allocate-instance are
> invoked on the class, but initialize-instance is invoked on the fresh
> instance. So initialize-instance is the proper generic function to
> specialize on.

Actually, as Madhu mentions and I realised shortly after I tried your 
suggestion, it's shared-initialize, since it catches redefinitions of 
classes as well.

And just to make sure, the proper way of vetoing class definition in 
shared-initialize of its class metaobject is as usual by signalling an 
error, right? Is it defined what state the class metaobject will be in, 
should the class redefinition fail?

Cheers,
Maciej
From: Pascal Costanza
Subject: Re: MOP problems -- a fairly basic case
Date: 
Message-ID: <5q318aFtmth0U1@mid.individual.net>
Maciej Katafiasz wrote:
> Den Thu, 15 Nov 2007 11:46:42 +0100 skrev Pascal Costanza:
> 
>> Assume the following metaclass definition:
>>
>> (defclass my-metaclass (standard-class) ())
>>
>> This:
>>
>> (defclass foo ... (:metaclass my-metaclass))
>>
>> results in a call to this:
>>
>> (ensure-class 'foo ... :metaclass my-metaclass ...)
>>
>> which results, among other things, in an invocation of
>> ensure-class-using-class and ultimately this:
>>
>> (make-instance 'my-metaclass ...)
>>
>> According to the specification of make-instance this means:
>>
>> (make-instance (find-class 'my-metaclass) ...)
>>
>> The class my-metaclass itself is an instance of standard-class (there
>> was no :metaclass option in the definition of my-metaclass to the
>> contrary), so methods on make-instance specialized on my-metaclass won't
>> trigger.
> 
> However, foo is an object of class my-metaclass. Therefore its creation 
> should be done by the means of make-instance, shouldn't it?

Make-instance is called on the class, not on the object. So for the 
dispatch of the make-instance function it doesn't matter what class the 
object has. The object actually doesn't even exist yet.

What matters is what class the class is an instance of.

Consider the base-level case:

(defclass moo () ())

(defmethod make-instance ((class moo) &rest initargs)
   (print "trigger me"))

(defvar *moo* (make-instance 'moo))

So what we get here is this:

(make-instance 'moo) =>
(make-instance (find-class 'moo))

If the method for make-instance should trigger, the object passed to it 
should be an instance of class 'moo. But (typep (find-class 'moo) 'moo) 
returns nil, so it doesn't trigger.

It's exactly the same for the meta-level case.

> Although actually there doesn't seem to be specified any protocol that 
> would spell out what steps are _required_ for conforming creation of an 
> instance, at least I'm unable to find it. There are specified in CLHS 
> access points that _allow_ one to create instances, but it's not said 
> which ones _will_ be invoked on all creation. Or am I reading it wrong?

It's correct that there are other ways to create instances. You could 
actually just call allocate-instance and initialize-instance manually.

The CLOS MOP, however, does specify that the creation of the class 
metaboject initiated by a defclass form goes through make-instance.

Whatever the case is, you should specialize on initialize-instance and 
perhaps on reinitialize-instance.

>>>> As a workaround, specialize on initialize-instance instead of
>>>> make-instance...
>>> Will try that, thanks.
>> According to the specification of make-instance (Section 7.1.7 of the
>> HyperSpec), you can see that make-instance and allocate-instance are
>> invoked on the class, but initialize-instance is invoked on the fresh
>> instance. So initialize-instance is the proper generic function to
>> specialize on.
> 
> Actually, as Madhu mentions and I realised shortly after I tried your 
> suggestion, it's shared-initialize, since it catches redefinitions of 
> classes as well.

You're not allowed to specialize shared-initialize for metaclasses.

> And just to make sure, the proper way of vetoing class definition in 
> shared-initialize of its class metaobject is as usual by signalling an 
> error, right? Is it defined what state the class metaobject will be in, 
> should the class redefinition fail?

You can veto the initialization and reinitialization of metaobject in 
initialize-instance and reinitialize-instance. If you veto 
reinitialize-instance, then the metaobject will be in the same state it 
was before, except for whatever preceding :before and :around methods 
have already done.

If you want to veto the creation of a metaobject, you have to hook on 
allocate-instance - that's the only way to allocate instances, so 
everyone has to go through that function.


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Maciej Katafiasz
Subject: Re: MOP problems -- a fairly basic case
Date: 
Message-ID: <fhhkr9$b04$3@news.net.uni-c.dk>
Den Thu, 15 Nov 2007 14:52:10 +0100 skrev Pascal Costanza:

> If the method for make-instance should trigger, the object passed to it
> should be an instance of class 'moo. But (typep (find-class 'moo) 'moo)
> returns nil, so it doesn't trigger.

Oooh, that needed some time to sink. This finally made me realise what's 
going on:

CL-USER> (make-instance (make-instance 'foo))
trigger me: #<FOO {B2C37D9}>
NIL
 
>> Actually, as Madhu mentions and I realised shortly after I tried your
>> suggestion, it's shared-initialize, since it catches redefinitions of
>> classes as well.
> 
> You're not allowed to specialize shared-initialize for metaclasses.

Noted. I can't find that passage, though, we're talking about http://
www.lisp.org/mop/concepts.html#init-class , right?
 
>> And just to make sure, the proper way of vetoing class definition in
>> shared-initialize of its class metaobject is as usual by signalling an
>> error, right? Is it defined what state the class metaobject will be in,
>> should the class redefinition fail?
> 
> You can veto the initialization and reinitialization of metaobject in
> initialize-instance and reinitialize-instance. If you veto
> reinitialize-instance, then the metaobject will be in the same state it
> was before, except for whatever preceding :before and :around methods
> have already done.

My question was rather "do I have to keep any guarantees about the state 
of the object, or can I clobber it if I signal the error?"
 
> If you want to veto the creation of a metaobject, you have to hook on
> allocate-instance - that's the only way to allocate instances, so
> everyone has to go through that function.

I see. Given that my classes will have the notion of "well-defined", it 
makes sense to hook into allocate-instance and veto the process as soon 
as possible, right? Though I'll have to hook into reinitialize-instance 
in any case, to handle redefinitions. So either way I get two mehods.

Cheers,
Maciej
From: Maciej Katafiasz
Subject: Re: MOP problems -- a fairly basic case
Date: 
Message-ID: <fhhl66$b04$4@news.net.uni-c.dk>
Den Thu, 15 Nov 2007 14:24:41 +0000 skrev Maciej Katafiasz:

> Noted. I can't find that passage, though, we're talking about http://
> www.lisp.org/mop/concepts.html#init-class , right?

Nope, it's http://www.lisp.org/mop/dictionary.html#%3C/a%3E

I'm interested in what the rationale behind forbidding SHARED-INITIALIZE 
is.

Cheers,
Maciej
From: Pascal Costanza
Subject: Re: MOP problems -- a fairly basic case
Date: 
Message-ID: <5q33uvFtn1m7U1@mid.individual.net>
Maciej Katafiasz wrote:
> Den Thu, 15 Nov 2007 14:24:41 +0000 skrev Maciej Katafiasz:
> 
>> Noted. I can't find that passage, though, we're talking about http://
>> www.lisp.org/mop/concepts.html#init-class , right?
> 
> Nope, it's http://www.lisp.org/mop/dictionary.html#%3C/a%3E
> 
> I'm interested in what the rationale behind forbidding SHARED-INITIALIZE 
> is.

You're actually not even allowed to define primary methods on 
initialize-instance and reinitialize-instance. The reason is that a CLOS 
  implementation is thus given a lot more freedom on how to actually 
construct metaobjects in hopefully more efficient ways than with a naive 
approach. Note that if you were allowed to define primary methods here, 
or define methods on shared-initialize, it's not clear in what state the 
metaobjects actually are, and what side effects have or haven't already 
been performed (like informing dependent metaobjects), and so on. 
Specifying all these details would be very wordy and cumbersome, and 
would put a lot of constraints on CLOS implementations.

A metaobject protocol is a contract between implementors and extenders. 
You need a balance somewhere between total freedom for implementors and 
total freedom for extenders. You can't have both.

I recommend reading the book "The Art of the Metaobject Protocol" - you 
will learn a lot more about such issues...


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Maciej Katafiasz
Subject: Re: MOP problems -- a fairly basic case
Date: 
Message-ID: <fhhm1a$b04$5@news.net.uni-c.dk>
Den Thu, 15 Nov 2007 15:38:23 +0100 skrev Pascal Costanza:

> You're actually not even allowed to define primary methods on
> initialize-instance and reinitialize-instance. The reason is that a CLOS
>   implementation is thus given a lot more freedom on how to actually
> construct metaobjects in hopefully more efficient ways than with a naive
> approach. Note that if you were allowed to define primary methods here,
> or define methods on shared-initialize, it's not clear in what state the
> metaobjects actually are, and what side effects have or haven't already
> been performed (like informing dependent metaobjects), and so on.
> Specifying all these details would be very wordy and cumbersome, and
> would put a lot of constraints on CLOS implementations.
> 
> A metaobject protocol is a contract between implementors and extenders.
> You need a balance somewhere between total freedom for implementors and
> total freedom for extenders. You can't have both.
> 
> I recommend reading the book "The Art of the Metaobject Protocol" - you
> will learn a lot more about such issues...

Yup. I just needed to start working on my MOP-using code before I could 
finish reading all the introductory chapters.

Cheers,
Maciej
From: Pascal Costanza
Subject: Re: MOP problems -- a fairly basic case
Date: 
Message-ID: <5q33mlFu1p2dU1@mid.individual.net>
Maciej Katafiasz wrote:

>>> Actually, as Madhu mentions and I realised shortly after I tried your
>>> suggestion, it's shared-initialize, since it catches redefinitions of
>>> classes as well.
>> You're not allowed to specialize shared-initialize for metaclasses.
> 
> Noted. I can't find that passage, though, we're talking about http://
> www.lisp.org/mop/concepts.html#init-class , right?

No, it's at http://www.lisp.org/mop/dictionary.html#%3C/a%3E (last 
paragraph).

>>> And just to make sure, the proper way of vetoing class definition in
>>> shared-initialize of its class metaobject is as usual by signalling an
>>> error, right? Is it defined what state the class metaobject will be in,
>>> should the class redefinition fail?
>> You can veto the initialization and reinitialization of metaobject in
>> initialize-instance and reinitialize-instance. If you veto
>> reinitialize-instance, then the metaobject will be in the same state it
>> was before, except for whatever preceding :before and :around methods
>> have already done.
> 
> My question was rather "do I have to keep any guarantees about the state 
> of the object, or can I clobber it if I signal the error?"

That depends on the semantics of your metaclass. Since it's yours, you 
can do whatever you want. ;)

Just make sure that you document it correctly.

>> If you want to veto the creation of a metaobject, you have to hook on
>> allocate-instance - that's the only way to allocate instances, so
>> everyone has to go through that function.
> 
> I see. Given that my classes will have the notion of "well-defined", it 
> makes sense to hook into allocate-instance and veto the process as soon 
> as possible, right? Though I'll have to hook into reinitialize-instance 
> in any case, to handle redefinitions. So either way I get two mehods.

It's a bad idea to have badly initialized objects floating around, for 
initial creation it's probably best to hook into allocate-instance if 
you cannot otherwise guarantee well-definedness. With reinitialization, 
you could indeed ensure that the object remains well-defined.

But maybe it's possible to achieve well-definedness in 
initialize-instance even if the user passes bad initialization 
arguments. For example, you could define a restart that offers some 
default settings...


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Madhu
Subject: Re: MOP problems -- a fairly basic case
Date: 
Message-ID: <m3r6iriud6.fsf@robolove.meer.net>
* Maciej Katafiasz  <············@news.net.uni-c.dk> :

| (defclass serialisable (standard-class)
|   ())
|
| (defmethod make-instance :after ((class serialisable) &rest args)
|   (format t "Packet: ~A" args))

[This may be TMI] but if what you are looking at doing in this method is
something like ensuring CLASS is a subclass of a SERIALIZBLE-OBJECT
class, (which is typically what you would want to do in persistence
frameworks), you could use an AROUND method on SHARED-INITIALIZE:

;; from my personal serializer lib:
(defmethod shared-initialize :around ((class serializable) slot-names &rest args &key direct-superclasses)
  "Ensure CLASS is a subclass of a SERIALIZBLE-OBJECT."
  (let ((persistent-metaclass (find-class 'serializable))
        (persistent-object (find-class serializable-object)))
    (cond ((or (eq class persistent-object)
               (loop for superclass in direct-superclasses
                     if (eq (class-of superclass) persistent-metaclass)
                     return t))
           (call-next-method))
          (t (remf args :direct-superclasses)
             (apply #'call-next-method class slot-names
                    :direct-superclasses
                    (cons persistent-object direct-superclasses)
                    args)))))

| (defmethod sb-mop:validate-superclass ((class serialisable)
| 	   			      (superclass standard-class))
|   t)
|
| (defmacro defpacket (&body body)
|   `(defclass ,@body
|        (:metaclass serialisable)))

--
Madhu
From: Pascal Costanza
Subject: Re: MOP problems -- a fairly basic case
Date: 
Message-ID: <5q2vqcFu0qflU1@mid.individual.net>
Madhu wrote:
> * Maciej Katafiasz  <············@news.net.uni-c.dk> :
> 
> | (defclass serialisable (standard-class)
> |   ())
> |
> | (defmethod make-instance :after ((class serialisable) &rest args)
> |   (format t "Packet: ~A" args))
> 
> [This may be TMI] but if what you are looking at doing in this method is
> something like ensuring CLASS is a subclass of a SERIALIZBLE-OBJECT
> class, (which is typically what you would want to do in persistence
> frameworks), you could use an AROUND method on SHARED-INITIALIZE:
> 
> ;; from my personal serializer lib:
> (defmethod shared-initialize :around ((class serializable) slot-names &rest args &key direct-superclasses)
>   "Ensure CLASS is a subclass of a SERIALIZBLE-OBJECT."
>   (let ((persistent-metaclass (find-class 'serializable))
>         (persistent-object (find-class serializable-object)))
>     (cond ((or (eq class persistent-object)
>                (loop for superclass in direct-superclasses
>                      if (eq (class-of superclass) persistent-metaclass)
>                      return t))
>            (call-next-method))
>           (t (remf args :direct-superclasses)
>              (apply #'call-next-method class slot-names
>                     :direct-superclasses
>                     (cons persistent-object direct-superclasses)
>                     args)))))

There are some mistakes in here:

- You are not allowed to define methods on shared-initialize for 
metaclasses. (See "Initialization of Class Metaobjects" in the CLOS MOP 
specification.) So instead, you have to define two methods on 
initialize-instance and reinitialize-instance respectively.

- If :direct-superclasses is not explicitly passed to 
reinitialize-instance, you're not supposed to change anything for that 
parameter. (Same section in the CLOS MOP spec.)

- It's not sufficient that one of the direct superclasses is an instance 
of serializable-class, but it could be that a direct superclass inherits 
from serializable-object in some other way.

Also some notes:

- I think that it's too complicated that serializable-object is an 
instance of serializable-class. serializable-object may as well be just 
a standard-class.

- You don't need to remf the old direct-superclasses, the overriding 
semantics for keyword arguments already takes care of ensuring that the 
correct value is seen.

Here is my version of the above definitions:

(defclass serializable-object (standard-object) ())
(defclass serializable-class  (standard-class)  ())

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

(defmethod initialize-instance :around
   ((class serializable-class) &rest initargs
    &key direct-superclasses &allow-other-keys)
   (declare (dynamic-extent initargs))
   (if (loop for direct-superclass in direct-superclasses
             thereis (ignore-errors
                       (subtypep direct-superclass
                                 (find-class 'serializable-object))))
     (call-next-method)
     (apply #'call-next-method
            class
            :direct-superclasses
            (append direct-superclasses
                    (list (find-class 'serializable-object)))
            initargs)))

(defmethod reinitialize-instance :around
   ((class serializable-class) &rest initargs
    &key (direct-superclasses '() direct-superclasses-p)
    &allow-other-keys)
   (declare (dynamic-extent initargs))
   (if (or (not direct-superclasses-p)
           (loop for direct-superclass in direct-superclasses
                 thereis (ignore-errors
                           (subtypep direct-superclass
                                     (find-class 'serializable-object))))
     (call-next-method)
     (apply #'call-next-method
            class
            :direct-superclasses
            (append direct-superclasses
                    (list (find-class 'serializable-object)))
            initargs)))


Note: I use ignore-errors for the subtypep test because clisp thinks 
that a forward referenced class cannot be a subtypep of anything.



Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Madhu
Subject: Re: MOP problems -- a fairly basic case
Date: 
Message-ID: <m3ir43ihc7.fsf@robolove.meer.net>
* Pascal Costanza
| Madhu wrote:
|> (defmethod shared-initialize :around ((class serializable) slot-names &rest args &key direct-superclasses)
[...]
| There are some mistakes in here:

Thanks Pascal!  For pointing out the bugs, and your suggestions.

| - You are not allowed to define methods on shared-initialize for
| metaclasses. (See "Initialization of Class Metaobjects" in the CLOS
| MOP specification.) So instead, you have to define two methods on
| initialize-instance and reinitialize-instance respectively.
|
| - If :direct-superclasses is not explicitly passed to
| reinitialize-instance, you're not supposed to change anything for that
| parameter. (Same section in the CLOS MOP spec.)

[I haven't been able to spot/infer this one in that section as yet,
still looking...]

| - It's not sufficient that one of the direct superclasses is an
| instance of serializable-class, but it could be that a direct
| superclass inherits from serializable-object in some other way.

The scheme was to automatically add SERIALIZABLE-OBJECT to the
superclasses of any class which is defined to be an instance of
SERIALIZABLE-CLASS.  So if any direct superclass was an instance of
SERIALIZABLE-CLASS it would already have SERIALIZABLE-OBJECT as a
superclass.  Since SERIALIZABLE-CLASS is the only metaclass the EQ test
	(eq (class-of direct-superclass) (find-class 'serializable-class))
would have sufficed in determining whether or not to add
SERIALIZABLE-OBJECT to the superclasses.  Is this right or am I missing
something?
--
Madhu
From: Pascal Costanza
Subject: Re: MOP problems -- a fairly basic case
Date: 
Message-ID: <5q3dr9Ftnh79U1@mid.individual.net>
Madhu wrote:
> * Pascal Costanza
> | Madhu wrote:
> |> (defmethod shared-initialize :around ((class serializable) slot-names &rest args &key direct-superclasses)
> [...]
> | There are some mistakes in here:
> 
> Thanks Pascal!  For pointing out the bugs, and your suggestions.
> 
> | - You are not allowed to define methods on shared-initialize for
> | metaclasses. (See "Initialization of Class Metaobjects" in the CLOS
> | MOP specification.) So instead, you have to define two methods on
> | initialize-instance and reinitialize-instance respectively.
> |
> | - If :direct-superclasses is not explicitly passed to
> | reinitialize-instance, you're not supposed to change anything for that
> | parameter. (Same section in the CLOS MOP spec.)
> 
> [I haven't been able to spot/infer this one in that section as yet,
> still looking...]

Paragraph #7.

> | - It's not sufficient that one of the direct superclasses is an
> | instance of serializable-class, but it could be that a direct
> | superclass inherits from serializable-object in some other way.
> 
> The scheme was to automatically add SERIALIZABLE-OBJECT to the
> superclasses of any class which is defined to be an instance of
> SERIALIZABLE-CLASS.  So if any direct superclass was an instance of
> SERIALIZABLE-CLASS it would already have SERIALIZABLE-OBJECT as a
> superclass.  Since SERIALIZABLE-CLASS is the only metaclass the EQ test
> 	(eq (class-of direct-superclass) (find-class 'serializable-class))
> would have sufficed in determining whether or not to add
> SERIALIZABLE-OBJECT to the superclasses.  Is this right or am I missing
> something?

How do you know that there is no (or will never be a) subclass of 
serializable-class?


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Madhu
Subject: Re: MOP problems -- a fairly basic case
Date: 
Message-ID: <m33av6iz23.fsf@robolove.meer.net>
* Pascal Costanza in (5q3dr9Ftnh79U1 @ mid.individual.net )
[...]
|> | - If :direct-superclasses is not explicitly passed to
|> | reinitialize-instance, you're not supposed to change anything for that
|> | parameter. (Same section in the CLOS MOP spec.)
|>
|> [I haven't been able to spot/infer this one in that section as yet,
|> still looking...]
|
| Paragraph #7.
|
|> | - It's not sufficient that one of the direct superclasses is an
|> | instance of serializable-class, but it could be that a direct
|> | superclass inherits from serializable-object in some other way.
|>
|> The scheme was to automatically add SERIALIZABLE-OBJECT to the
|> superclasses of any class which is defined to be an instance of
|> SERIALIZABLE-CLASS.  So if any direct superclass was an instance of
|> SERIALIZABLE-CLASS it would already have SERIALIZABLE-OBJECT as a
|> superclass.  Since SERIALIZABLE-CLASS is the only metaclass the EQ test
|> 	(eq (class-of direct-superclass) (find-class 'serializable-class))
|> would have sufficed in determining whether or not to add
|> SERIALIZABLE-OBJECT to the superclasses.  Is this right or am I missing
|> something?
|
| How do you know that there is no (or will never be a) subclass of
| serializable-class?

Indeed.  As usual, your comments have been valuable,
--
Madhu