From: Marco Baringer
Subject: adding a super class
Date: 
Message-ID: <m2sm8x70ih.fsf@bese.it>
i'm attempting to use the MOP to ensure that classes defined with a
certain super class always have another class in their class
precedence list. here's what i've come up with so far [the mopp
package is just the implementation's MOP package]:

(defclass kclass (standard-class)
  ())

(defmethod mopp:validate-superclass ((sub kclass) (super standard-class))
  t)

(defclass super ()
  ()
  (:metaclass kclass))

(defmethod mopp:compute-class-precedence-list ((class kclass))
  (let ((cpl (call-next-method)))
    (if (member (find-class 'super) cpl)
        cpl
        (list* (first cpl) (find-class 'super) (cdr cpl)))))

(defclass foo ()
  ()
  (:metaclass kclass))

(defclass bar (foo)
  ()
  (:metaclass kclass))

When attempting te define the BAR class i get the error: Inconsistent
superclasses for #<KCLASS BAR>

if i manually insert super in the defclass form for FOO everything
works as expected, if i attempt to trace
mopp:compute-class-precedence-list i see that the return value when
defining the class FOO is the same with or without SUPER in the
defclass form for FOO. i've read the mop a couple of times and
attempted to find examples on google to no avail. what am i missing?

-- 
-Marco
Ring the bells that still can ring.
Forget your perfect offering.
There is a crack in everything.
That's how the light gets in.
     -Leonard Cohen

From: Simon Katz
Subject: Re: adding a super class
Date: 
Message-ID: <dvatl0tuf04ib0rbh9vn8adiasecqp2m1k@4ax.com>
On Sat, 02 Oct 2004 14:44:22 +0200, "Marco Baringer" <··@bese.it>
wrote:

>
>i'm attempting to use the MOP to ensure that classes defined with a
>certain super class always have another class in their class
>precedence list. here's what i've come up with so far [the mopp
>package is just the implementation's MOP package]:
>
>(defclass kclass (standard-class)
>  ())
>
>(defmethod mopp:validate-superclass ((sub kclass) (super standard-class))
>  t)
>
>(defclass super ()
>  ()
>  (:metaclass kclass))
>
>(defmethod mopp:compute-class-precedence-list ((class kclass))
>  (let ((cpl (call-next-method)))
>    (if (member (find-class 'super) cpl)
>        cpl
>        (list* (first cpl) (find-class 'super) (cdr cpl)))))
>
>(defclass foo ()
>  ()
>  (:metaclass kclass))
>
>(defclass bar (foo)
>  ()
>  (:metaclass kclass))
>
>When attempting te define the BAR class i get the error: Inconsistent
>superclasses for #<KCLASS BAR>
>
>if i manually insert super in the defclass form for FOO everything
>works as expected, if i attempt to trace
>mopp:compute-class-precedence-list i see that the return value when
>defining the class FOO is the same with or without SUPER in the
>defclass form for FOO. i've read the mop a couple of times and
>attempted to find examples on google to no avail. what am i missing?

I tried your code with LispWorks 4.3.7 after changing MOPP to CLOS.

No problem there.

Perhaps a bug in your implementation.

___________________
Real email address:
(substitute ··@ #\+ (substitute #\s #\! "u!enet001+nomi!tech.com"))
From: Pascal Costanza
Subject: Re: adding a super class
Date: 
Message-ID: <cjmc38$4v4$1@newsreader2.netcologne.de>
Marco Baringer wrote:
> i'm attempting to use the MOP to ensure that classes defined with a
> certain super class always have another class in their class
> precedence list. here's what i've come up with so far [the mopp
> package is just the implementation's MOP package]:

I prefer this approach:

(defclass kobject (standard-object)
   ())

(defclass kclass (standard-class)
   ())

(defmethod initialize-instance
            ((class kclass)
             &rest initargs
             &key direct-superclasses
             &allow-other-keys)
   (declare (dynamic-extent initargs))
   (apply #'call-next-method
          class
          :direct-superclasses
          (adjoin (find-class 'kobject)
                  direct-superclasses)
          initargs))

(defmethod reinitialize-instance
            ((class kclass)
             &rest initargs
             &key (direct-superclasses nil direct-superclasses-p)
             &allow-other-keys)
   (declare (dynamic-extent initargs))
   (apply #'call-next-method
          class
          (if direct-superclasses-p
              (list* :direct-superclasses
                     (adjoin (find-class 'kobject)
                             direct-superclasses)
                     initargs)
            initargs)))


Pascal

-- 
Tyler: "How's that working out for you?"
Jack: "Great."
Tyler: "Keep it up, then."
From: Kenny Tilton
Subject: Re: adding a super class
Date: 
Message-ID: <L0A7d.3477$4C.1176247@twister.nyc.rr.com>
Pascal Costanza wrote:
> 
> Marco Baringer wrote:
> 
>> i'm attempting to use the MOP to ensure that classes defined with a
>> certain super class always have another class in their class
>> precedence list. here's what i've come up with so far [the mopp
>> package is just the implementation's MOP package]:
> 
> 
> I prefer this approach:
> 
> (defclass kobject (standard-object)
>   ())
> 
> (defclass kclass (standard-class)
>   ())
> 
> (defmethod initialize-instance
>            ((class kclass)
>             &rest initargs
>             &key direct-superclasses
>             &allow-other-keys)
>   (declare (dynamic-extent initargs))
>   (apply #'call-next-method
>          class
>          :direct-superclasses
>          (adjoin (find-class 'kobject)
>                  direct-superclasses)
>          initargs))

What if kobject is an indirect superclass? the spec said "in the cpl", 
not "amongst the direct superclasses". (And to check the cpl one has to 
wait for inheritance finalization, so this cannot be achieved here.)

kenny

-- 
Cells? Cello? Celtik?: http://www.common-lisp.net/project/cells/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
From: Pascal Costanza
Subject: Re: adding a super class
Date: 
Message-ID: <cjmqln$s3j$1@newsreader2.netcologne.de>
Kenny Tilton wrote:

> What if kobject is an indirect superclass? the spec said "in the cpl", 
> not "amongst the direct superclasses". (And to check the cpl one has to 
> wait for inheritance finalization, so this cannot be achieved here.)

I don't understand the problem. Classes are always at most once in the 
class precedence list. If a class is already in the class precedence 
list of one of the direct superclasses it doesn't cause a dramatic 
effect to add it once more, does it?

Maybe I am missing something, could you explain this in more detail?


Pascal

-- 
Tyler: "How's that working out for you?"
Jack: "Great."
Tyler: "Keep it up, then."
From: Kenny Tilton
Subject: Re: adding a super class
Date: 
Message-ID: <TKC7d.3487$4C.1234440@twister.nyc.rr.com>
Pascal Costanza wrote:
> 
> Kenny Tilton wrote:
> 
>> What if kobject is an indirect superclass? the spec said "in the cpl", 
>> not "amongst the direct superclasses". (And to check the cpl one has 
>> to wait for inheritance finalization, so this cannot be achieved here.)
> 
> 
> I don't understand the problem. Classes are always at most once in the 
> class precedence list. If a class is already in the class precedence 
> list of one of the direct superclasses it doesn't cause a dramatic 
> effect to add it once more, does it?

I don't know. Aren't you in danger of making A a subclass of B where B 
is a subclass of A? Maybe I need more coffee[*]. But if so:

(progn
   (defclass top ()())
   (defclass required (top)())
   (defclass mid-1 (required)())
   (defclass mid-2 (mid-1)())
   (defclass bott (required mid-2)()) ;; imagine required added
   (finalize-inheritance (find-class 'bott))
   (class-precedence-list (find-class 'bott)))

Error: While computing the class precedence list of the class named BOTT.
It is not possible to compute the class precedence list because
there is a circularity in the local precedence relations.
This arises because:
   the class named MID-1 appears in the supers of the class named MID-2
   the class named MID-2 follows the class named MID-1 in the supers of 
the class named BOTT.
[condition type: PROGRAM-ERROR]


kenny

[1] Or maybe listening to Dubya a coupla nights ago caused my upper 
cortex to shutdown to protect itself and it is still rebooting.

k

-- 
Cells? Cello? Celtik?: http://www.common-lisp.net/project/cells/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
From: Pascal Costanza
Subject: Re: adding a super class
Date: 
Message-ID: <cjmvui$740$1@newsreader2.netcologne.de>
Kenny Tilton wrote:
> 
> Pascal Costanza wrote:
> 
>>
>> Kenny Tilton wrote:
>>
>>> What if kobject is an indirect superclass? the spec said "in the 
>>> cpl", not "amongst the direct superclasses". (And to check the cpl 
>>> one has to wait for inheritance finalization, so this cannot be 
>>> achieved here.)
>>
>> I don't understand the problem. Classes are always at most once in the 
>> class precedence list. If a class is already in the class precedence 
>> list of one of the direct superclasses it doesn't cause a dramatic 
>> effect to add it once more, does it?
> 
> I don't know. Aren't you in danger of making A a subclass of B where B 
> is a subclass of A?

You're right. Damn - now I need to rewrite some of my code. :(


Pascal

-- 
Tyler: "How's that working out for you?"
Jack: "Great."
Tyler: "Keep it up, then."
From: marco
Subject: Re: adding a super class
Date: 
Message-ID: <m2sm8x54t1.fsf@bese.it>
Pascal Costanza <········@web.de> writes:

> Maybe I am missing something, could you explain this in more detail?

as far as i can tell (which probbaly isn't very far):

messing with direct-superclasses causes this:

(defclass foo ()
  ()
  (:metaclass klass))

to be equivalent to:

(defclass foo (super-class)
  ())

which is great. but in the case of:

(defclass bar (foo)
  ()
  (:metaclass klass))

we have the equivalent of:

(defclass bar (foo super-class)
  ())

which isn't completly what i want (though it's close as far as chosing
the right method is concerned). things get worse when you (or i as the
case may be) mess up the code and end up with:

(defclass bar (super-class foo)
  ())

:(

-- 
-Marco
Ring the bells that still can ring.
Forget your perfect offering.
There is a crack in everything.
That's how the light gets in.
     -Leonard Cohen
From: Marco Baringer
Subject: Re: adding a super class
Date: 
Message-ID: <m2fz4x6vnv.fsf@bese.it>
Pascal Costanza <········@web.de> writes:

> Marco Baringer wrote:
>> i'm attempting to use the MOP to ensure that classes defined with a
>> certain super class always have another class in their class
>> precedence list. here's what i've come up with so far [the mopp
>> package is just the implementation's MOP package]:
>
> I prefer this approach:

why?

p.s. - the use of adjoin (as oppsode to (append (remove-if ...) ...)
)generates circular c-p-l errors on cmucl (the same issue arises on
sbcl but it doesn't signal an error).

-- 
-Marco
Ring the bells that still can ring.
Forget your perfect offering.
There is a crack in everything.
That's how the light gets in.
     -Leonard Cohen
From: Pascal Costanza
Subject: Re: adding a super class
Date: 
Message-ID: <cjmgr0$ce7$1@newsreader2.netcologne.de>
Marco Baringer wrote:
> Pascal Costanza <········@web.de> writes:
> 
>>Marco Baringer wrote:
>>
>>>i'm attempting to use the MOP to ensure that classes defined with a
>>>certain super class always have another class in their class
>>>precedence list. here's what i've come up with so far [the mopp
>>>package is just the implementation's MOP package]:
>>
>>I prefer this approach:
> 
> why?

I can't tell you exactly why but I think my code is conceptually 
cleaner. (Take this with a grain of salt.)

As far as I understand the MOP, the purpose of 
compute-class-precedence-list is to be able to use different strategies 
to resolve multiple inheritance hierarchies, not to inject new 
superclasses. By adding an additional superclass in 
(re)initialize-instance, I am not talking about such strategies, but 
only about the local properties of a class.

> p.s. - the use of adjoin (as oppsode to (append (remove-if ...) ...)
> )generates circular c-p-l errors on cmucl (the same issue arises on
> sbcl but it doesn't signal an error).

I can imagine this to be the case when you do this in 
compute-class-precedence-list, but why should this be the case when I am 
only modifying the list of direct superclasses?


Pascal

-- 
Tyler: "How's that working out for you?"
Jack: "Great."
Tyler: "Keep it up, then."
From: Pascal Costanza
Subject: Re: adding a super class
Date: 
Message-ID: <cjp2hb$mmj$1@newsreader2.netcologne.de>
Marco Baringer wrote:
> Pascal Costanza <········@web.de> writes:
> 
>>Marco Baringer wrote:
>>
>>>i'm attempting to use the MOP to ensure that classes defined with a
>>>certain super class always have another class in their class
>>>precedence list. here's what i've come up with so far [the mopp
>>>package is just the implementation's MOP package]:
>>
>>I prefer this approach:
> 
> why?

...ok, I have checked both with AMOP and with Andreas Paepcke's paper. I 
haven't found any example in AMOP, but Paepcke has one very specific 
example (and that's where I got my approach from, only that I tend to 
forget the sources of my ideas ;). His code looks as follows:

(defmethod initialize-instance :around
   ((class persisten-metalevel-class)
    &rest all-keys
    &key direct-superclasses)
   (let ((root-class (find-class 'persistence-root-class))
         (pobjs-mc (find-class 'persisten-metalevel-class)))
     (if (member-if
          #'(lambda (super)
              (eq (class-of super) pobjs-mc))
          direct-superclasses)
       (call-next-method)
       (apply #'call-next-method
              class
              :direct-superclasses (append direct-superclasses
                                           (list root-class))
              all-keys))))

First of all, there seem to be some mistakes in his code. For example, 
he doesn't say &allow-other-keys.

What this code does is that it checks whether one of the direct 
superclasses of the class to be initialized is already an instance of 
the respective metaclass. If so, there is no need to add the required 
superclass, otherwise it is added to the end of the direct superclasses.

When I adapted this code I have made the following changes: I didn't 
include the test because I have thought that it doesn't matter whether a 
class is mentioned more than once in all the lists of direct 
superclasses. Furthermore, I have used adjoin instead of append because 
I have thought that it doesn't matter whether I add a class in front or 
at the end (at least for the examples I had in mind).

In fact, instead of append I first used cons which gave me an error in 
some cases - when the class is already in the list of direct 
superclasses. The same error should also be raised in Paepcke's code.

Another problem in Paepcke's code is that the check of the direct 
superclasses' classes a) uses eq instead of subtypep and more 
importantly b) doesn't work if they are forward-referenced classes. (So 
Kenny is right that the complete information one needs is only available 
at class finalization time. That's probably also the reason why Gregor 
Kiczales wanted to get rid of forward-referenced classes at some stage - 
at least that's what he said in some newsgroup posting about a decade ago.)

Possible workarounds I currently see:

1) Use Paepcke's code still, fix the bugs, and raise an error if none of 
the direct superclasses is already a subtype of the respective metaclass 
and some of them are forward-referenced classes.

2) Override compute-class-precedence-list to ensure that the required 
superclass is in the cpl.

I'd prefer 1) because it is more declarative (you only say what you 
want, not how you achieve it). However, I don't completely understand 
yet what the effect of appending the required class to the list of 
direct superclasses is.

Comments?


Pascal

-- 
Tyler: "How's that working out for you?"
Jack: "Great."
Tyler: "Keep it up, then."
From: Kalle Olavi Niemitalo
Subject: Re: adding a super class
Date: 
Message-ID: <87fz4vg17g.fsf@Astalo.kon.iki.fi>
Pascal Costanza <········@web.de> writes:

> First of all, there seem to be some mistakes in his code. For example,
> he doesn't say &allow-other-keys.

I don't think it's necessary.  INITIALIZE-INSTANCE already has
&allow-other-keys at the generic-function level.  If I read
section 7.6.5 of CLHS correctly, this means any keyword argument
will be accepted regardless of whether individual methods also
have &allow-other-keys.
From: Kenny Tilton
Subject: Re: adding a super class
Date: 
Message-ID: <zVW7d.154759$4h7.26840591@twister.nyc.rr.com>
Kalle Olavi Niemitalo wrote:
> Pascal Costanza <········@web.de> writes:
> 
> 
>>First of all, there seem to be some mistakes in his code. For example,
>>he doesn't say &allow-other-keys.
> 
> 
> I don't think it's necessary.  INITIALIZE-INSTANCE already has
> &allow-other-keys at the generic-function level.

Nope. It looks that way from CLHS, but it is not so.

kt

-- 
Cells? Cello? Celtik?: http://www.common-lisp.net/project/cells/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
From: Kalle Olavi Niemitalo
Subject: Re: adding a super class
Date: 
Message-ID: <87k6u7h7d7.fsf@Astalo.kon.iki.fi>
Kenny Tilton <·······@nyc.rr.com> writes:

> Kalle Olavi Niemitalo wrote:
>> I don't think it's necessary.  INITIALIZE-INSTANCE already has
>> &allow-other-keys at the generic-function level.
>
> Nope. It looks that way from CLHS, but it is not so.

Why is it not so?

CLHS specifies:
- gf: (instance &rest initargs &key &allow-other-keys)
- method: ((instance standard-object) &rest initargs)

CLISP 2.33.2 has:
- gf: (instance &rest initargs &key &allow-other-keys)
- method: ((instance standard-object) &rest initargs)
- method: ((instance structure-object) &rest initargs)
- method: ((new-class-object standard-class) &rest args &key
           name (metaclass <standard-class>) documentation
           direct-superclasses direct-slots direct-default-initargs)
- method: ((new-class-object structure-class) &rest args &key
           name (metaclass <structure-class>) documentation
           direct-superclasses direct-slots direct-default-initargs
           names slots size)

SBCL 0.8.14.9 has:
- gf: (instance &rest initargs)
- method: ((instance sb-pcl::slot-object) &rest initargs)
- method: :after ((gf standard-generic-function)
                  &key (lambda-list nil lambda-list-p)
                       argument-precedence-order)

The AMOP proposes:
- gf: (instance &key)                                 ; pages 31 & 309
- method: ((instance standard-object) &rest initargs) ; pages 31 & 309
- method: :after ((class standard-class) &key
                  direct-superclasses direct-slots)   ; page 23

Are these discrepancies not bugs?  According to the CLHS, the
user may define methods on INITIALIZE-INSTANCE, and I think this
implies such methods may rely on the &allow-other-keys specified
for the generic function.  Because of the restrictions on
user-defined methods, the implementor might be able to move the
&allow-other-keys from the generic function to a method that is
always applicable; but it still ought to be _somewhere_.
From: Kenny Tilton
Subject: Re: adding a super class
Date: 
Message-ID: <vg38d.154781$4h7.27030868@twister.nyc.rr.com>
Kalle Olavi Niemitalo wrote:

> Kenny Tilton <·······@nyc.rr.com> writes:
> 
> 
>>Kalle Olavi Niemitalo wrote:
>>
>>>I don't think it's necessary.  INITIALIZE-INSTANCE already has
>>>&allow-other-keys at the generic-function level.
>>
>>Nope. It looks that way from CLHS, but it is not so.
> 
> 
> Why is it not so?
> 
> CLHS specifies:
> - gf: (instance &rest initargs &key &allow-other-keys)

Where do you see that? I see:

7.7.36 initialize-instance 	Standard Generic Function

Syntax:
     initialize-instance instance &rest initargs &key &allow-other-keys 
    instance

That says "syntax". Yes, I can specify &allow-other-keys if I like. That 
is syntax. Not a GF definition.

kenny

-- 
Cells? Cello? Celtik?: http://www.common-lisp.net/project/cells/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
From: Kalle Olavi Niemitalo
Subject: Re: adding a super class
Date: 
Message-ID: <873c0vaz7t.fsf@Astalo.kon.iki.fi>
Kenny Tilton <·······@nyc.rr.com> writes:

> Syntax:
>      initialize-instance instance &rest initargs &key
>      &allow-other-keys   instance
>
> That says "syntax".

According to 1.4.4.20, "The ``Syntax'' description for a generic
function describes the lambda list of the generic function itself".
Does this not mean that an &allow-other-keys listed in the "Syntax"
is required to be present in the lambda list?
From: Pascal Costanza
Subject: Re: adding a super class
Date: 
Message-ID: <cjr0me$m8$1@newsreader2.netcologne.de>
Kalle Olavi Niemitalo wrote:
> Kenny Tilton <·······@nyc.rr.com> writes:
> 
> 
>>Syntax:
>>     initialize-instance instance &rest initargs &key
>>     &allow-other-keys   instance
>>
>>That says "syntax".
> 
> According to 1.4.4.20, "The ``Syntax'' description for a generic
> function describes the lambda list of the generic function itself".
> Does this not mean that an &allow-other-keys listed in the "Syntax"
> is required to be present in the lambda list?

Section 7.1.2 wouldn't make sense if all keywords were accepted by default.


Pascal

-- 
Tyler: "How's that working out for you?"
Jack: "Great."
Tyler: "Keep it up, then."
From: Kenny Tilton
Subject: Re: adding a super class
Date: 
Message-ID: <G1b8d.154796$4h7.27120179@twister.nyc.rr.com>
Pascal Costanza wrote:
> 
> Kalle Olavi Niemitalo wrote:
> 
>> Kenny Tilton <·······@nyc.rr.com> writes:
>>
>>
>>> Syntax:
>>>     initialize-instance instance &rest initargs &key
>>>     &allow-other-keys   instance
>>>
>>> That says "syntax".
>>
>>
>> According to 1.4.4.20, "The ``Syntax'' description for a generic
>> function describes the lambda list of the generic function itself".
>> Does this not mean that an &allow-other-keys listed in the "Syntax"
>> is required to be present in the lambda list?

Kalle, do you see (defgeneric initialize-instance...) somewhere in the 
CLHS. The entry says i-i is a GF. It says the syntax allows one to 
specify &a-o-k. It does not say (defgeneric...)

Do you know of an implementation in which i-i behaves as if &a-o-k was 
specified in some implementation defgeneric form? I do not. Would you 
want that? Hell no, then every mistake specifying initargs to 
make-instance would go uncaught.

What exactly is the problem?

> 
> 
> Section 7.1.2 wouldn't make sense if all keywords were accepted by default.

Especially the bit: "The default value for :allow-other-keys is nil."

:)

kenny

-- 
Cells? Cello? Celtik?: http://www.common-lisp.net/project/cells/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
From: Kalle Olavi Niemitalo
Subject: Re: adding a super class
Date: 
Message-ID: <87y8im7afn.fsf@Astalo.kon.iki.fi>
Kenny Tilton <·······@nyc.rr.com> writes:

> Kalle, do you see (defgeneric initialize-instance...) somewhere in the
> CLHS.

No, I don't.

> The entry says i-i is a GF. It says the syntax allows one to
> specify &a-o-k.

Allows whom to specify &a-o-k?  The user who calls initialize-instance,
or the implementor who defines it as a generic function?

> Do you know of an implementation in which i-i behaves as if &a-o-k was
> specified in some implementation defgeneric form?

Like I posted before, CLISP has &allow-other-keys there.

> I do not. Would you want that? Hell no, then every mistake
> specifying initargs to make-instance would go uncaught.

Not if the initargs are checked by a method, separately from the
usual &KEY feature.
 
> Especially the bit: "The default value for :allow-other-keys is nil."

That is just a way to control the method that checks the initargs.
Perhaps the behavior would have been clearer if it had been called
:allow-other-initargs, and the name :allow-other-keys had been
reserved to the implicit validation done by &KEY.
From: Kenny Tilton
Subject: Re: adding a super class
Date: 
Message-ID: <xGe8d.154800$4h7.27151753@twister.nyc.rr.com>
Kalle Olavi Niemitalo wrote:
> Kenny Tilton <·······@nyc.rr.com> writes:
> 
> 
>>Kalle, do you see (defgeneric initialize-instance...) somewhere in the
>>CLHS.
> 
> 
> No, I don't.
> 
> 
>>The entry says i-i is a GF. It says the syntax allows one to
>>specify &a-o-k.
> 
> 
> Allows whom to specify &a-o-k?  The user who calls initialize-instance,
> or the implementor who defines it as a generic function?

The user who defines a /method/ on initialize-instance.

> 
> 
>>Do you know of an implementation in which i-i behaves as if &a-o-k was
>>specified in some implementation defgeneric form?
> 
> 
> Like I posted before, CLISP has &allow-other-keys there.

Right, sorry.

> 
> 
>>I do not. Would you want that? Hell no, then every mistake
>>specifying initargs to make-instance would go uncaught.
> 
> 
> Not if the initargs are checked by a method, separately from the
> usual &KEY feature.
>  

Impossible without MOP support, and MOP support is not standard or even 
universal.

kenny

-- 
Cells? Cello? Celtik?: http://www.common-lisp.net/project/cells/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
From: Kalle Olavi Niemitalo
Subject: Re: adding a super class
Date: 
Message-ID: <87pt3y76rc.fsf@Astalo.kon.iki.fi>
Kenny Tilton <·······@nyc.rr.com> writes:

> Kalle Olavi Niemitalo wrote:
>> Not if the initargs are checked by a method, separately from the
>> usual &KEY feature.
>
> Impossible without MOP support, and MOP support is not standard or
> even universal.

The method is provided by the CLOS implementor, who can freely
use non-standard features of her implementation.
From: Kalle Olavi Niemitalo
Subject: Re: adding a super class
Date: 
Message-ID: <871xge8psy.fsf@Astalo.kon.iki.fi>
Pascal Costanza <········@web.de> writes:

> Section 7.1.2 wouldn't make sense if all keywords were accepted by default.

It makes sense if unsupported initialization arguments are first
accepted by the generic function but then explicitly rejected by
a method, like the first paragraph of section 7.1.2 states.

Consider also that the :INITARG slot option makes keywords valid.
If the mechanism described in section 7.6.5 were the only way in
which initargs are validated, DEFCLASS would have to automatically
define methods on several generic functions in order to mark the
declared initarg keywords as valid.
From: Pascal Costanza
Subject: Re: adding a super class
Date: 
Message-ID: <cjrv1r$109e$1@f1node01.rhrz.uni-bonn.de>
Kalle Olavi Niemitalo wrote:

> Pascal Costanza <········@web.de> writes:
> 
>>Section 7.1.2 wouldn't make sense if all keywords were accepted by default.
> 
> It makes sense if unsupported initialization arguments are first
> accepted by the generic function but then explicitly rejected by
> a method, like the first paragraph of section 7.1.2 states.

OK, so maybe Andreas Paepcke did use such a CLOS implementation. I get 
your point. Thanks.


Pascal

-- 
Pascal Costanza               University of Bonn
···············@web.de        Institute of Computer Science III
http://www.pascalcostanza.de  R�merstr. 164, D-53117 Bonn (Germany)
From: Pascal Costanza
Subject: Re: adding a super class
Date: 
Message-ID: <cjpn6o$qfv$1@newsreader2.netcologne.de>
Pascal Costanza wrote:

> Possible workarounds I currently see:
> 
> 1) Use Paepcke's code still, fix the bugs, and raise an error if none of 
> the direct superclasses is already a subtype of the respective metaclass 
> and some of them are forward-referenced classes.
> 
> 2) Override compute-class-precedence-list to ensure that the required 
> superclass is in the cpl.
> 
> I'd prefer 1) because it is more declarative (you only say what you 
> want, not how you achieve it). However, I don't completely understand 
> yet what the effect of appending the required class to the list of 
> direct superclasses is.

I have played around with both variants today in my AspectL code, and I 
have finally decided against option 1). The reason is that 
class-direct-superclasses will return the implicitly added class 
although one hasn't mentioned it in the original definition. 
Conceptually, the implicitly added class is not a direct superclass, or 
that's what I think by now. [1]

So I am now using code similar to the one I have posted yesterday (-> 
adjoin-class-before, but renamed because it is in fact a more general 
function).

Marco, thanks for posting the problem. I have learnt something important!



Pascal


[1] In AspectL, methods like class-direct-superclasses are used to 
reconstruct the original class definition in the al.mixins package. 
That's why I needed to use adjoin instead of just cons, because in later 
redefinitions I may get passed a direct superclass that was previously 
added only implicitly...

-- 
Tyler: "How's that working out for you?"
Jack: "Great."
Tyler: "Keep it up, then."
From: Pascal Costanza
Subject: Re: adding a super class
Date: 
Message-ID: <cjn0pq$8cs$1@newsreader2.netcologne.de>
Marco Baringer wrote:
> i'm attempting to use the MOP to ensure that classes defined with a
> certain super class always have another class in their class
> precedence list.

OK, what about this one?

(defun adjoin-class-before (class1 class2 list)
   (loop for class in list
         if (eq class class1) return list
         else if (eq class class2) collect class1
         end
         collect class))

(defmethod compute-class-precedence-list (class)
   (adjoin-class-before
    (find-class 'my-required-superclass)
    (find-class 'standard-object)
    (call-next-method)))


Pascal

-- 
Tyler: "How's that working out for you?"
Jack: "Great."
Tyler: "Keep it up, then."
From: Larry Clapp
Subject: Re: adding a super class
Date: 
Message-ID: <slrncm09g5.2uo.larry@theclapp.ddts.net>
In article <··············@bese.it>, Marco Baringer wrote:
> i'm attempting to use the MOP to ensure that classes defined with a
> certain super class always have another class in their class
> precedence list.

Why would you add the class to the class-precedence-list, instead of
throwing an error when you don't find it?  Just adding it seems like
an invitation to bugs.

-- 
Larry Clapp / ·····@theclapp.org
From: Pascal Costanza
Subject: Re: adding a super class
Date: 
Message-ID: <cjpmjp$pka$1@newsreader2.netcologne.de>
Larry Clapp wrote:
> In article <··············@bese.it>, Marco Baringer wrote:
> 
>>i'm attempting to use the MOP to ensure that classes defined with a
>>certain super class always have another class in their class
>>precedence list.
> 
> Why would you add the class to the class-precedence-list, instead of
> throwing an error when you don't find it?  Just adding it seems like
> an invitation to bugs.

Programmers' convenience. Similar to the way how standard-object is 
implicitly added if your list of direct superclasses is empty.


Pascal

-- 
Tyler: "How's that working out for you?"
Jack: "Great."
Tyler: "Keep it up, then."
From: Bruno Haible
Subject: Re: adding a super class
Date: 
Message-ID: <cjub59$31$1@laposte.ilog.fr>
> (defmethod mopp:compute-class-precedence-list ((class kclass))
>   (let ((cpl (call-next-method)))
>     (if (member (find-class 'super) cpl)
>       cpl
>       (list* (first cpl) (find-class 'super) (cdr cpl)))))

This is not valid: According to the MOP's description of
compute-class-precedence-list, "The result is a list which contains
each of class and its superclasses once and only once."
But here you return #<class super> as part of the list, although it is
not a superclass (of class FOO for example).

Bruno
From: Simon Katz
Subject: Re: adding a super class
Date: 
Message-ID: <mpg5m0h64fenu8t0rluvg4ip0p71bl1cg3@4ax.com>
On 5 Oct 2004 14:30:01 GMT, Bruno Haible <·····@clisp.org> wrote:

>> (defmethod mopp:compute-class-precedence-list ((class kclass))
>>   (let ((cpl (call-next-method)))
>>     (if (member (find-class 'super) cpl)
>>       cpl
>>       (list* (first cpl) (find-class 'super) (cdr cpl)))))
>
>This is not valid: According to the MOP's description of
>compute-class-precedence-list, "The result is a list which contains
>each of class and its superclasses once and only once."
>But here you return #<class super> as part of the list, although it is
>not a superclass (of class FOO for example).
>
>Bruno

Isn't it COMPUTE-CLASS-PRECEDENCE-LIST that /defines/ the
superclasses?

The standard COMPUTE-CLASS-PRECEDENCE-LIST adds STANDARD-OBJECT and T.
Is adding SUPER any different?

Mind you, I think the only example of adding in extra superclasses
I've seen from an authority is the one in Andreas Paepcke's
"User-Level Language Crafting" where he adds to the direct-supers in
INITIALIZE-INSTANCE. (I think a similar hack in REINITIALIZE-INSTANCE
is also needed.) If the work could instead be done by
COMPUTE-CLASS-PRECEDENCE-LIST that would be simpler, so I do wonder if
there's a problem with that.

Also, adding SUPER in COMPUTE-CLASS-PRECEDENCE-LIST works fine if
there are forward-referenced classes, so that has to be preferable if
it is valid.

Simon.
From: Pascal Costanza
Subject: Re: adding a super class
Date: 
Message-ID: <cjuhco$gh$1@newsreader2.netcologne.de>
Simon Katz wrote:

> The standard COMPUTE-CLASS-PRECEDENCE-LIST adds STANDARD-OBJECT and T.

No, this is specified to be done in INITIALIZE-INSTANCE and 
REINITIALIZE-INSTANCE. See "Initialization of Class Metaobjects".


Pascal

-- 
Tyler: "How's that working out for you?"
Jack: "Great."
Tyler: "Keep it up, then."
From: Simon Katz
Subject: Re: adding a super class
Date: 
Message-ID: <3jk5m0hmkrvn5ad7f59mk9fjluf8jo702e@4ax.com>
On Tue, 05 Oct 2004 18:16:22 +0200, Pascal Costanza <········@web.de>
wrote:

>
>Simon Katz wrote:
>
>> The standard COMPUTE-CLASS-PRECEDENCE-LIST adds STANDARD-OBJECT and T.
>
>No, this is specified to be done in INITIALIZE-INSTANCE and 
>REINITIALIZE-INSTANCE. See "Initialization of Class Metaobjects".
>
>
>Pascal

Ah, sorry for that. (I won't even try to explain how I arrived at that
wrong conclusion.)

Simon.
___________________
Real email address:
(substitute ··@ #\+ (substitute #\s #\! "u!enet001+nomi!tech.com"))
From: Pascal Costanza
Subject: Re: adding a super class
Date: 
Message-ID: <cjuguv$t7c$1@newsreader2.netcologne.de>
Bruno Haible wrote:
>>(defmethod mopp:compute-class-precedence-list ((class kclass))
>>  (let ((cpl (call-next-method)))
>>    (if (member (find-class 'super) cpl)
>>      cpl
>>      (list* (first cpl) (find-class 'super) (cdr cpl)))))
> 
> This is not valid: According to the MOP's description of
> compute-class-precedence-list, "The result is a list which contains
> each of class and its superclasses once and only once."
> But here you return #<class super> as part of the list, although it is
> not a superclass (of class FOO for example).

Hm, one could read that statement as a minimal requirement which would 
imply that the list could contain more than those classes.

Would a deviation from (different interpretations of) that rule have an 
impact on CLOS implementations?

I also don't fully grasp the meaning of the following statement: "All 
methods on this generic function must compute the class precedence list 
as a function of the ordered direct superclasses of the superclasses of 
class."

The "superclasses of class" is already the transitive closure of the 
direct superclass relationship, isn't it? However, this would make the 
part "direct superclasses of the superclasses of class" redundant. 
What's going on here?


Pascal

-- 
Tyler: "How's that working out for you?"
Jack: "Great."
Tyler: "Keep it up, then."
From: Simon Katz
Subject: Re: adding a super class
Date: 
Message-ID: <ell5m0takbu4t3apu629q650060qe0ikfp@4ax.com>
On Tue, 05 Oct 2004 18:09:01 +0200, Pascal Costanza <········@web.de>
wrote:

>
>Bruno Haible wrote:
>>>(defmethod mopp:compute-class-precedence-list ((class kclass))
>>>  (let ((cpl (call-next-method)))
>>>    (if (member (find-class 'super) cpl)
>>>      cpl
>>>      (list* (first cpl) (find-class 'super) (cdr cpl)))))
>> 
>> This is not valid: According to the MOP's description of
>> compute-class-precedence-list, "The result is a list which contains
>> each of class and its superclasses once and only once."
>> But here you return #<class super> as part of the list, although it is
>> not a superclass (of class FOO for example).
>
>Hm, one could read that statement as a minimal requirement which would 
>imply that the list could contain more than those classes.

Yes, I agree. But I've just found this:

AMOP section 3.4, just after "We are now ready to document the new
piece of protocol", says:
  "The result must be a list of class metaobjects; the
   first element of the list must be <class> itself, the
   last two elements must be standard-object and t, in
   that order, and the other elements must be a
   permutation of the other superclasses of <class>".

The phrase "permutation of the other superclasses" suggests strongly
that "the other superclasses" are already a given, and if that's so
it's clear that extra supers cannot be added.

[A proviso: The quote is from chapter 3 of AMOP which is not part of
the CLOS MOP specification, so I guess it could be argued that it does
not necessarily apply. I searched for "permutation" at the two main
pages hanging off http://www.lisp.org/mop/contents.html, but could not
find an equivalent statement.]

BTW, is it defined somewhere that "superclasses" means the transitive
closure of the direct superclasses? I had previously thought (probably
without justification) that the superclasses were defined by the the
value returned by COMPUTE-CLASS-PRECEDENCE-LIST, but then the words
I've quoted from AMOP wouldn't make sense.

>Would a deviation from (different interpretations of) that rule have an 
>impact on CLOS implementations?

(My knowledge of the MOP is far too sketchy to have any idea.)

>I also don't fully grasp the meaning of the following statement: "All 
>methods on this generic function must compute the class precedence list 
>as a function of the ordered direct superclasses of the superclasses of 
>class."
>
>The "superclasses of class" is already the transitive closure of the 
>direct superclass relationship, isn't it? However, this would make the 
>part "direct superclasses of the superclasses of class" redundant. 
>What's going on here?

I think it may be expressed clumsily and wrongly.

Assuming that "superclasses" means the transitive closure of the
direct superclasses, I'm interpreting this as
  "... a function of
   (1) the ordered direct superclasses of class, and
   (2) the ordered direct superclasses of each of class's
       superclasses".
(So if class has N superclasses, this is a function of N+1 things,
each of which is an ordered list of classes.)
This differs from the original text by adding (1) and adding "each" to
(2). The CLHS glossary entry for "superclass" explicitly says "No
class is a superclass of itself", so I think it's necessary to add
(1).

Does that make better sense?

Simon.
___________________
Real email address:
(substitute ··@ #\+ (substitute #\s #\! "u!enet001+nomi!tech.com"))
From: Pascal Costanza
Subject: Re: adding a super class
Date: 
Message-ID: <cjv2i4$sag$1@newsreader2.netcologne.de>
Simon Katz wrote:

> The phrase "permutation of the other superclasses" suggests strongly
> that "the other superclasses" are already a given, and if that's so
> it's clear that extra supers cannot be added.

OK, maybe. I am not 100% sure here whether this wouldn't still allow to 
add one's stuff, but I have found another indication to the contrary: 
Exercise 3.1 in AMOP explicitly suggests to use a method 
INITIALIZE-INSTANCE to provide a default superclass for certain 
metaclasses and the solution in the appendix uses the same technique as 
Paepcke's paper. So these indications strongly suggest that the MOP 
designers want us to use that approach. Still not clear to me how to 
handle forward referenced classes. I will try to find a solution for that.

> BTW, is it defined somewhere that "superclasses" means the transitive
> closure of the direct superclasses?

That's usually the idea in OOP. I don't think that CLOS deviates from it.

> Assuming that "superclasses" means the transitive closure of the
> direct superclasses, I'm interpreting this as
>   "... a function of
>    (1) the ordered direct superclasses of class, and
>    (2) the ordered direct superclasses of each of class's
>        superclasses".
[...]

> Does that make better sense?

Yes, thanks a lot.


Pascal

-- 
Tyler: "How's that working out for you?"
Jack: "Great."
Tyler: "Keep it up, then."
From: Simon Katz
Subject: Re: adding a super class
Date: 
Message-ID: <ga76m05g0vttgkpn3r1dg23931hj2g1qd2@4ax.com>
On Tue, 05 Oct 2004 23:09:24 +0200, Pascal Costanza <········@web.de>
wrote:

> Still not clear to me how to handle forward referenced
> classes. I will try to find a solution for that.

If you do find a solution, I'd be very interested to hear about it.

>> BTW, is it defined somewhere that "superclasses" means the transitive
>> closure of the direct superclasses?
>
>That's usually the idea in OOP. I don't think that CLOS deviates from it.

Sorry -- I wasn't explicit enough. What I was trying to get at was
this:

If you are allowed to add extra superclasses in
COMPUTE-CLASS-PRECEDENCE-LIST, the superclasses would /not/ be the
transitive closure of the direct superclasses.

So, if somewhere "superclasses" is /defined/ as the transitive
closure of the direct superclasses, that would mean you are not
allowed to add extra superclasses in COMPUTE-CLASS-PRECEDENCE-LIST.
___________________
Real email address:
(substitute ··@ #\+ (substitute #\s #\! "u!enet001+nomi!tech.com"))
From: John Thingstad
Subject: Re: adding a super class
Date: 
Message-ID: <opsff0vxvfpqzri1@mjolner.upc.no>
On Tue, 05 Oct 2004 23:34:48 +0100, Simon Katz <·······@nomistech.com>  
wrote:

>
> So, if somewhere "superclasses" is /defined/ as the transitive
> closure of the direct superclasses, that would mean you are not
> allowed to add extra superclasses in COMPUTE-CLASS-PRECEDENCE-LIST.
> ___________________
> Real email address:
> (substitute ··@ #\+ (substitute #\s #\! "u!enet001+nomi!tech.com"))

Seems to me, by the time you have a exact solution you will have the  
answer.
That is the nature of Lisp ;)

-- 
Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
From: Pascal Costanza
Subject: Re: adding a super class
Date: 
Message-ID: <ck1do5$o40$1@f1node01.rhrz.uni-bonn.de>
Simon Katz wrote:

> On Tue, 05 Oct 2004 23:09:24 +0200, Pascal Costanza <········@web.de>
> wrote:
> 
>>Still not clear to me how to handle forward referenced
>>classes. I will try to find a solution for that.
> 
> If you do find a solution, I'd be very interested to hear about it.

Here is what I came up with by now. Since this code is rather tedious, 
I'd be interested in suggestions for simplification.

Some notes: The first cond-clause checks whether one of the direct 
superclasses is either the required superclass or an instance of the 
metaclass in question. In both cases, the required superclass will 
already become part of the class precedence list. The second clause 
tests whether standard-object is among the direct superclasses. 
LispWorks passes standard-object as a direct superclass when the 
respective list in the defclass form is empty. It does this as part of 
the expansion of the defclass form (contrary to the MOP spec, I think), 
so this is the only way to catch that case, as far as I can see. The 
third clause checks for forward referenced classes. Since the code can't 
predict the future, this signals an error in case. If none of the 
clauses mentioned above apply, the required superclass is appended to 
the (possibly empty) list of given direct superclasses. In the latter 
case, the given class is a topmost class for the given metaclass in the 
class hierarchy.

I have only tested the code under LispWorks for Macintosh 4.3.7, but I 
believe I have obeyed all the rules of the MOP specification. So I hope 
this works on other CL implementations as well.

Any comments?


Pascal


(defun initialize-class-metaobject
        (class metaclass required-superclass
               direct-superclasses initargs call-next)
   (if direct-superclasses
       (cond ((some (lambda (direct-superclass)
                      (or (eq direct-superclass required-superclass)
                          (typep direct-superclass metaclass)))
                    direct-superclasses)
              (funcall call-next))

             ((member (find-class 'standard-object) direct-superclasses
                      :test #'eq)
              (apply call-next class
                     :direct-superclasses (substitute
                                           required-superclass
                                           (find-class 'standard-object)
                                           direct-superclasses)
                     initargs))

             ((some (lambda (direct-superclass)
                      (typep direct-superclass
                             'forward-referenced-class))
                    direct-superclasses)
              (cerror
               "Ask for an assumption."
               "Some of the direct superclasses of class ~S are forward 
referenced and none of the others are instances of ~S. However, it must 
be unambiguous whether some of the direct superclasses will turn out as 
instances of ~S or none of them will." class metaclass metaclass)
              (if (y-or-n-p
                   "Will some of the direct superclasses of ~S turn out 
as instances of ~S?"
                   class metaclass)
                  (funcall call-next)
                (apply call-next class
                       :direct-superclasses
                       (append direct-superclasses
                               (list required-superclass))
                       initargs)))

             (t (apply call-next class
                       :direct-superclasses
                       (append direct-superclasses
                               (list required-superclass))
                       initargs)))
     (apply call-next class
            :direct-superclasses (list required-superclass)
            args)))

(defclass myobject (standard-object)
   ())

(defclass myclass (standard-class)
   ())

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

(defmethod initialize-instance :around
   ((class myclass)
    &rest args
    &key direct-superclasses
    &allow-other-keys)
   (declare (dynamic-extent args))
   (initialize-class-metaobject
    class (find-class 'myclass) (find-class 'myobject)
    direct-superclasses args #'call-next-method))

(defmethod reinitialize-instance :around
   ((class myclass)
    &rest args
    &key (direct-superclasses nil direct-superclasses-p)
    &allow-other-keys)
   (declare (dynamic-extent args))
   (if direct-superclasses-p
       (initialize-class-metaobject
        class (find-class 'myclass) (find-class 'myobject)
        direct-superclasses args #'call-next-method)
     (call-next-method)))

(defclass test1 ()
   ()
   (:metaclass myclass))

(defclass test2 (test1)
   ()
   (:metaclass myclass))

(defclass test3 (myobject)
   ()
   (:metaclass myclass))

(defclass test4 (standard-object)
   ()
   (:metaclass myclass))

(defclass non-forward ()
   ())

(defclass test5 (non-forward)
   ()
   (:metaclass myclass))

(defclass test6 (forward)
   ()
   (:metaclass myclass))


-- 
Pascal Costanza               University of Bonn
···············@web.de        Institute of Computer Science III
http://www.pascalcostanza.de  R�merstr. 164, D-53117 Bonn (Germany)
From: Simon Katz
Subject: Re: adding a super class
Date: 
Message-ID: <her8m01ii1ceke1rr4qodqjeo2kktkfl3h@4ax.com>
On Wed, 06 Oct 2004 20:32:36 +0200, Pascal Costanza <········@web.de>
wrote:

>Simon Katz wrote:
>
>> On Tue, 05 Oct 2004 23:09:24 +0200, Pascal Costanza <········@web.de>
>> wrote:
>> 
>>>Still not clear to me how to handle forward referenced
>>>classes. I will try to find a solution for that.
>> 
>> If you do find a solution, I'd be very interested to hear about it.
>
>Here is what I came up with by now. Since this code is rather tedious, 
>I'd be interested in suggestions for simplification.

First, a comment and question on your code:

I changed ARGS to INITARGS at the end of INITIALIZE-CLASS-METAOBJECT,
and ran your code with no further problems (using LWW 4.3.7). My
knowledge of the MOP isn't really sufficient to give you very
meaningful comments, but to the extent that I do understand it your
code looks ok to me.

My question is: Why do you handle the direct superclasses differently
in INITIALIZE-INSTANCE and REINITIALIZE-INSTANCE? (I guess I'm asking
for some MOP tutorial here...)

Now, back to the Big Picture...

I don't like the idea of requesting input from the user when
processing the class definitions. I'd prefer to forbid
forward-referenced classes.

I wonder if this would work:
- Assume initially that any forward-referenced classes are
  not instances of the special metaclass.
- When a forward-referenced instance of the special metaclass is
  actually defined (call this class c), adjust the direct
  superclasses of each of c's direct subclasses to no longer
  have the special superclass as a direct superclass.

Unfortunately I don't have the time at the moment to extend my
knowledge of the MOP to the level where I could think this through
properly.
___________________
Real email address:
(substitute ··@ #\+ (substitute #\s #\! "u!enet001+nomi!tech.com"))
From: Simon Katz
Subject: Re: adding a super class
Date: 
Message-ID: <7e19m05g1bssskvn4mkpt095om39hd79s6@4ax.com>
I wrote:

>I wonder if this would work:
>- Assume initially that any forward-referenced classes are
>  not instances of the special metaclass.
>- When a forward-referenced instance of the special metaclass is
>  actually defined (call this class c), adjust the direct
>  superclasses of each of c's direct subclasses to no longer
>  have the special superclass as a direct superclass.

That last clause should be: adjust the direct superclasses of each of
c's direct subclasses to no longer include the special superclass.
___________________
Real email address:
(substitute ··@ #\+ (substitute #\s #\! "u!enet001+nomi!tech.com"))
From: Pascal Costanza
Subject: Re: adding a super class
Date: 
Message-ID: <ck6nb6$kk$1@newsreader2.netcologne.de>
Simon Katz wrote:
> I wrote:
> 
>>I wonder if this would work:
>>- Assume initially that any forward-referenced classes are
>> not instances of the special metaclass.
>>- When a forward-referenced instance of the special metaclass is
>> actually defined (call this class c), adjust the direct
>> superclasses of each of c's direct subclasses to no longer
>> have the special superclass as a direct superclass.
> 
> That last clause should be: adjust the direct superclasses of each of
> c's direct subclasses to no longer include the special superclass.

I guess it works. Here's the code.

Note: I don't see a better way to specialize CHANGE-CLASS. The current 
version requires you to define another method on CHANGE-CLASS for each 
subclass of MYCLASS. (We're not talking about instance of MYCLASS, so 
things are not terribly bad here.)

A method on UPDATE-INSTANCE-FOR-DIFFERENT-CLASS would have worked, but 
it is not called in LispWorks and AMOP explicitly specifies it to not 
work on class metaobjects. AMOP also doesn't specify how classes changed 
from FORWARD-REFERENCED-CLASS are indeed reinitialized, so specializing 
CHANGE-CLASS seems to be the safest bet.

There would things to comment about the rest of the code, but I am tired 
now, so I'll just wait for questions. ;)

I will incorporate the code into AspectL, and this will be a good check 
whether the code is portable. (That's why I have written explicit 
(re)initialize-class-metaobject functions. Also note that I have 
switched the last two arguments to those functions in comparison to the 
earlier version because this better accomodates another change of the 
initargs that I need in AspectL.)

Ah yes, and I think I have found a good use for &aux... ;)

Pascal


(setf (find-class 'test6) nil)
(setf (find-class 'forward) nil)

(defvar *preliminary-modified-classes* (make-hash-table :test #'equal))

(defun initialize-class-metaobject
        (class metaclass required-superclass direct-superclasses
         call-next initargs
         &aux class-key (list class metaclass))
   (remhash class-key *preliminary-modified-classes*)
   (if (some (lambda (direct-superclass)
               (typep direct-superclass metaclass))
             direct-superclasses)
       (apply call-next class
              :direct-superclasses direct-superclasses
              initargs)
     (progn
       (when (some (lambda (direct-superclass)
                     (typep direct-superclass 'forward-referenced-class))
                   direct-superclasses)
         (setf (gethash class-key *preliminary-modified-classes*)
               direct-superclasses))
       (apply call-next class
              :direct-superclasses
              (append (remove (find-class 'standard-object)
                              direct-superclasses :test #'eq)
                      (list required-superclass))
              initargs))))

(defun reinitialize-class-metaobject
        (class metaclass required-superclass direct-superclasses
         call-next initargs
         &aux class-key (list class metaclass))
   (declare (ignore direct-superclasses))
   (let ((org-direct-superclasses
          (gethash class-key *preliminary-modified-classes*)))
     (if org-direct-superclasses
         (initialize-class-metaobject
          class metaclass required-superclass org-direct-superclasses
          call-next initargs)
       (apply call-next class initargs))))

(defclass myobject (standard-object) ())
(defclass myclass (standard-class) ())

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

(defmethod initialize-instance :around
   ((class myclass)
    &rest args
    &key direct-superclasses
    &allow-other-keys)
   (declare (dynamic-extent args))
   (initialize-class-metaobject
    class (find-class 'myclass) (find-class 'myobject)
    direct-superclasses #'call-next-method args))

(defmethod reinitialize-instance :around
   ((class myclass)
    &rest args
    &key (direct-superclasses nil direct-superclasses-p)
    &allow-other-keys)
   (declare (dynamic-extent args))
   (funcall
    (if direct-superclasses-p
        #'initialize-class-metaobject
      #'reinitialize-class-metaobject)
    class (find-class 'myclass) (find-class 'myobject)
    direct-superclasses #'call-next-method args))

(defmethod change-class :after
   ((class forward-referenced-class)
    (new-metaclass (eql (find-class 'myclass))) &rest args)
   (declare (ignore args))
   (mapc #'reinitialize-instance (class-direct-subclasses class)))

(defclass test1 ()
   ()
   (:metaclass myclass))

(defclass test2 (test1)
   ()
   (:metaclass myclass))

(defclass test3 (myobject)
   ()
   (:metaclass myclass))

(defclass test4 (standard-object)
   ()
   (:metaclass myclass))

(defclass non-forward () ())

(defclass test5 (non-forward)
   ()
   (:metaclass myclass))

(defclass test6 (forward)
   ()
   (:metaclass myclass))
From: Simon Katz
Subject: Re: adding a super class
Date: 
Message-ID: <rtcfm0pdvd821qvd51r0glou57lhmpr1o0@4ax.com>
On Fri, 08 Oct 2004 20:47:04 +0200, Pascal Costanza <········@web.de>
wrote:

>
>Simon Katz wrote:
>> I wrote:
>> 
>>>I wonder if this would work:
>>>- Assume initially that any forward-referenced classes are
>>> not instances of the special metaclass.
>>>- When a forward-referenced instance of the special metaclass is
>>> actually defined (call this class c), adjust the direct
>>> superclasses of each of c's direct subclasses to no longer
>>> have the special superclass as a direct superclass.
>> 
>> That last clause should be: adjust the direct superclasses of each of
>> c's direct subclasses to no longer include the special superclass.
>
>I guess it works. Here's the code.

Great!

I changed two lines from this:
         &aux class-key (list class metaclass))
to this
         &aux (class-key (list class metaclass)))
and had a play.

Yes, it seems to work nicely.

Also, I read the code carefully and it all makes good sense to me.

>Note: I don't see a better way to specialize CHANGE-CLASS. The current 
>version requires you to define another method on CHANGE-CLASS for each 
>subclass of MYCLASS. (We're not talking about instance of MYCLASS, so 
>things are not terribly bad here.)

You might consider adding another level of metaclass and changing your
CHANGE-CLASS to specialize on MYMETACLASS:

(defclass mymetaclass (standard-class) ())

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defmethod validate-superclass
             ((class mymetaclass) (superclass standard-class)) t))

(defclass myclass (standard-class) () (:metaclass mymetaclass))

(defmethod change-class :after
   ((class forward-referenced-class)
    (new-metaclass mymetaclass) &rest args)
   (declare (ignore args))
   (mapc #'reinitialize-instance (class-direct-subclasses class)))


An example:

(defclass myclass-sub (standard-class) () (:metaclass mymetaclass))

(defclass x (forward-sub)
  ()
  (:metaclass myclass-sub))

(defclass forward ()
  ()
  (:metaclass myclass-sub))

(defclass forward-sub (forward)
  ()
  (:metaclass myclass-sub))

Simon.

Simon
___________________
Real email address:
(substitute ··@ #\+ (substitute #\s #\! "u!enet001+nomi!tech.com"))
From: Simon Katz
Subject: Re: adding a super class
Date: 
Message-ID: <lcefm0lbbvl27gohig9tn3b50u3hd2cqsm@4ax.com>
I wrote:

>An example:
>
>(defclass myclass-sub (standard-class) () (:metaclass mymetaclass))
                  ^^^^

Sorry, that -sub is misleading. We're now talking about instances of
MYMETACLASS rather than suclasses of MYCLASS.

Of course, this will work too:

(defclass myclass-sub (my-class) () (:metaclass mymetaclass))


___________________
Real email address:
(substitute ··@ #\+ (substitute #\s #\! "u!enet001+nomi!tech.com"))
From: Simon Katz
Subject: Re: adding a super class
Date: 
Message-ID: <k1hfm05lt7542470ck8ttav849sd667idd@4ax.com>
>I wrote:
>
>>An example:
>>
>>(defclass myclass-sub (standard-class) () (:metaclass mymetaclass))
>                  ^^^^
>
>Sorry, that -sub is misleading. We're now talking about instances of
>MYMETACLASS rather than suclasses of MYCLASS.
>
>Of course, this will work too:
>
>(defclass myclass-sub (my-class) () (:metaclass mymetaclass))


Hmm, I forgot about the rather important methods defined on MY-CLASS.

This will work, I think:

(defclass myclass-sub (myclass) () (:metaclass mymetaclass))

This won't work:

(defclass foo (standard-class) () (:metaclass mymetaclass))

Perhaps its possible to check that all instances of MYMETACLASS are
either MYCLASS or subclasses of MYCLASS, e.g. in an
INITIALIZE-INSTANCE :AFTER method.

I'm going to stop thinking about this now in case (a) I discover I've
made more mistakes which I then try to fix with further mistakes, or
(b) my brain explodes.

Sorry for the noise.

___________________
Real email address:
(substitute ··@ #\+ (substitute #\s #\! "u!enet001+nomi!tech.com"))
From: Pascal Costanza
Subject: Re: adding a super class
Date: 
Message-ID: <ck8hcs$228$1@newsreader2.netcologne.de>
Simon Katz wrote:

> Perhaps its possible to check that all instances of MYMETACLASS are
> either MYCLASS or subclasses of MYCLASS, e.g. in an
> INITIALIZE-INSTANCE :AFTER method.
> 
> I'm going to stop thinking about this now in case (a) I discover I've
> made more mistakes which I then try to fix with further mistakes, or
> (b) my brain explodes.
> 
> Sorry for the noise.

No, I think your idea is quite good. What we could do in an 
INITIALIZE-INSTANCE :AFTER method is to add the necessary methods to 
INITIALIZE-INSTANCE and REINITIALIZE-INSTANCE that make the rest of the 
protocol work. Then one could say the following:

(defclass myclass (standard-class)
   ()
   (:metaclass defaulted-standard-class)
   (:default-superclass myobject))

...and everything else would (hopefully) work. I'll try that...


Pascal

-- 
Tyler: "How's that working out for you?"
Jack: "Great."
Tyler: "Keep it up, then."
From: Pascal Costanza
Subject: Re: adding a super class
Date: 
Message-ID: <ck9d2t$h6g$1@newsreader2.netcologne.de>
Simon Katz wrote:
> On Fri, 08 Oct 2004 20:47:04 +0200, Pascal Costanza <········@web.de>
> wrote:

>>Note: I don't see a better way to specialize CHANGE-CLASS. The current 
>>version requires you to define another method on CHANGE-CLASS for each 
>>subclass of MYCLASS. (We're not talking about instance of MYCLASS, so 
>>things are not terribly bad here.)
> 
> You might consider adding another level of metaclass and changing your
> CHANGE-CLASS to specialize on MYMETACLASS:
[...]

I have found a simpler way. First of all, I have checked how various 
implementations realize the change from a forward referenced class to a 
"real" class. Indeed, they all call UPDATE-INSTANCE-FOR-DIFFERENT-CLASS 
and additionally REINITIALIZE-INSTANCE. This is somewhat redundant, but 
a natural result of the CLOS and MOP specifications. I seem to have 
missed the call of U-I-F-D-C in earlier tests, so forget what I said 
earlier about that part of the protocol.

I have tested several variants, and the following is obviously the one 
that works best:

(defmethod reinitialize-instance :after
   ((class myclass) &rest args)
   (declare (ignore args))
   (mapc #'reinitialize-instance (class-direct-subclasses class)))

There is no direct way to check here whether that reinitialization is 
issued by a switch from forward-referenced-class to myclass, so 
reinitialization of subclasses will theoretically happen more often than 
actually needed. In principle, it would be possible to communicate the 
necessary information from UPDATE-INSTANCE-FOR-DIFFERENT-CLASS, but I 
don't bother at the moment because I don't think this will lead to 
dramatic costs. REINITIALIZE-INSTANCE should be relatively cheap when it 
isn't passed any initialization arguments (at least the MOP spec says 
that it does more or less nothing in that case), and classes are usually 
seldomly redefined anyway.

Since we are already redefining REINITIALIZE-INSTANCE for MYCLASS, I 
have incorporated that method into the rest of my code. I have also 
simplified the rest of the interface, so that you now only have to 
specialize INITIALIZE-INSTANCE and REINITIALIZE-INSTANCE with simple 
calls to INITIALIZE-CLASS-METAOBJECT and REINITIALIZE-CLASS-METAOBJECT.

I have tested the code on five major Common Lisp implementations, so I 
am pretty confident by now that the code works correctly. This will 
become part of the next release of AspectL.

Here's the code.

(use-package #+allegro :mop
              #+cmu :clos-mop
              #+lispworks :clos
              #+openmcl :ccl
              #+sbcl :sb-mop)

(defvar *preliminary-modified-classes* (make-hash-table :test #'equal))

(defun initialize-class-metaobject
        (call-next class metaclass required-superclass
                   &rest initargs
                   &key direct-superclasses
                   &allow-other-keys)
   (declare (dynamic-extent initargs))
   (let ((class-key (list class metaclass)))
     (remhash class-key *preliminary-modified-classes*)
     (if (some (lambda (direct-superclass)
                 (subtypep direct-superclass required-superclass))
               direct-superclasses)
         (apply call-next class initargs)
       (progn
         (when (some (lambda (direct-superclass)
                       (typep direct-superclass
                              'forward-referenced-class))
                     direct-superclasses)
           (setf (gethash class-key *preliminary-modified-classes*)
                 direct-superclasses))
         (apply call-next class
                :direct-superclasses
                (append (remove (find-class 'standard-object)
                                direct-superclasses :test #'eq)
                        (list required-superclass))
                initargs)))))

(defun reinitialize-class-metaobject
        (call-next class metaclass required-superclass
                   &rest initargs
                   &key (direct-superclasses nil direct-superclasses-p)
                   &allow-other-keys)
   (declare (dynamic-extent initargs)
            (ignore direct-superclasses))
   (prog1
       (if direct-superclasses-p
           (apply #'initialize-class-metaobject call-next
                  class metaclass required-superclass initargs)
         (let* ((class-key (list class metaclass))
                (org-direct-superclasses
                 (gethash class-key *preliminary-modified-classes*)))
           (if org-direct-superclasses
               (apply #'initialize-class-metaobject call-next
                      class metaclass required-superclass
                      :direct-superclasses org-direct-superclasses
                      initargs)
             (apply call-next class initargs))))
     (mapc #'reinitialize-instance (class-direct-subclasses class))))

(defclass myobject (standard-object) ())
(defclass myclass (standard-class) ())

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

(defmethod initialize-instance :around
   ((class myclass) &rest args)
   (declare (dynamic-extent args))
   (apply #'initialize-class-metaobject #'call-next-method
          class (find-class 'myclass) (find-class 'myobject)
          args))

(defmethod reinitialize-instance :around
   ((class myclass) &rest args)
   (declare (dynamic-extent args))
   (apply #'reinitialize-class-metaobject #'call-next-method
          class (find-class 'myclass) (find-class 'myobject)
          args))

(defparameter *test-p* t)

(when *test-p*
   (setf (find-class 'test6) nil)
   (setf (find-class 'forward) nil)

   (defclass test1 ()
     ()
     (:metaclass myclass))

   (defclass test2 (test1)
     ()
     (:metaclass myclass))

   (defclass test3 (myobject)
     ()
     (:metaclass myclass))

   (defclass test4 (standard-object)
     ()
     (:metaclass myclass))

   (defclass non-forward () ())

   (defclass test5 (non-forward)
     ()
     (:metaclass myclass))

   (defclass test6 (forward)
     ()
     (:metaclass myclass))

   (defclass forward ()
     ()
     (:metaclass myclass))

   (assert (every (lambda (class-name)
                    #+allegro
                    (finalize-inheritance (find-class class-name))
                    (typep (class-prototype (find-class class-name))
                           'myobject))
                  '(test1 test2 test3 test4 test5 test6)))

   (assert (every (lambda (class-name-list)
                    (equal (class-direct-superclasses
                             (find-class (car class-name-list)))
                           (mapcar #'find-class (cdr class-name-list))))
                  '((test1 myobject)
                    (test2 test1)
                    (test3 myobject)
                    (test4 myobject)
                    (test5 non-forward myobject)
                    (test6 forward))))

   (print :done))
From: Pascal Costanza
Subject: Re: adding a super class
Date: 
Message-ID: <ck2nso$3uo$1@newsreader2.netcologne.de>
Simon Katz wrote:

> First, a comment and question on your code:
> 
> I changed ARGS to INITARGS at the end of INITIALIZE-CLASS-METAOBJECT,

Correct, that's a bug. In the meantime, I have found out that the 
initial test "(if direct-superclasses ..." is superfluous because it is 
already subsumed by the other cases.

> and ran your code with no further problems (using LWW 4.3.7). My
> knowledge of the MOP isn't really sufficient to give you very
> meaningful comments, but to the extent that I do understand it your
> code looks ok to me.

Ok, thanks.

> My question is: Why do you handle the direct superclasses differently
> in INITIALIZE-INSTANCE and REINITIALIZE-INSTANCE? (I guess I'm asking
> for some MOP tutorial here...)

No, that's not directly related to the MOP, but the protocol of 
REINITIALIZE-INSTANCE is to leave anything unchanged that isn't 
explicitly passed. So I would violate that contract if I modified the 
list of direct superclasses in that case.

> Now, back to the Big Picture...
> 
> I don't like the idea of requesting input from the user when
> processing the class definitions. I'd prefer to forbid
> forward-referenced classes.
> 
> I wonder if this would work:
> - Assume initially that any forward-referenced classes are
>   not instances of the special metaclass.
> - When a forward-referenced instance of the special metaclass is
>   actually defined (call this class c), adjust the direct
>   superclasses of each of c's direct subclasses to no longer
>   have the special superclass as a direct superclass.
> 
> Unfortunately I don't have the time at the moment to extend my
> knowledge of the MOP to the level where I could think this through
> properly.

This is an interesting idea. I'll see what I can do. Hopefully, this is 
covered by the dependent maintenance protocol, then this could work...


Pascal

-- 
Tyler: "How's that working out for you?"
Jack: "Great."
Tyler: "Keep it up, then."