From: Mark McConnell
Subject: change-class without losing mixins
Date: 
Message-ID: <d3aed052.0407211033.70ab6e8a@posting.google.com>
Say I have a class A1 that has a few slots.  I also have mixin classes
B, C, D.  Each of these has a few slots, all distinct from the slots
in the others and in A1.  Each of the classes "A1 with one mixin" has
a name, like

(defclass A1-with-B (B A1)
  ())

There may be classes like A1-with-BD or A1-with-BCD someday, but not
right now.

There is also a subclass A2 of A1 that adds a few slots to A1.

Let x be an instance of some class that's A1 with one or more of B, C,
D mixed in.  The problem is that, after a few minutes of computations,
I discover that x should really belong to A2.  Its essential nature is
A2-ness, but I had no way of knowing that until after the
computations.

I want to call (change-class x 'A2) with appropriate initializations
in an update-instance-for-different-class :after method.  However, my
understanding from CLHS 7.2.* is that change-class will throw away the
slots of B, C, and D, whichever were present.  I need x to retain
those slots even as it becomes an A2.

I don't want to define A2-with-B, etc.  That's partly because it's too
wordy.  But also, I don't know whether x will have 1 or 2 or 3 of the
mixins; why define A2-with-BCD before I know whether A1-with-BCD will
ever be needed?

Can you create a class precedence list on the fly?  Like an "anonymous
class", the union of A2 and "whatever x originally satisfied".  I can
imagine messy problems if A2's slots conflicted with BCD's slots, but
they don't.

Any pointers to books or online references would be appreciated.

From: Pascal Costanza
Subject: Re: change-class without losing mixins
Date: 
Message-ID: <cdmf27$bsn$1@newsreader2.netcologne.de>
Mark McConnell wrote:

> Can you create a class precedence list on the fly?  Like an "anonymous
> class", the union of A2 and "whatever x originally satisfied".  I can
> imagine messy problems if A2's slots conflicted with BCD's slots, but
> they don't.
> 
> Any pointers to books or online references would be appreciated.

What you try to do is clearly an example of a possible use of the 
metaobject protocol (MOP). The MOP defines the function ensure-class, 
among other things, with which you can programmatically create new 
classes. You need to throw in some intelligence to make sure that you 
create the right class combinations. The idea would be to have a hash 
table that maps sets of classes to (possibly anonymous) classes that 
have the given set in their class precedence list. Then you can lazily 
create new classes on the fly that subclass the right classes as requested.

If you don't want to use the MOP, you could work around it by using 
(eval `(defclass ...)). This should work AFAICS - however, I haven't 
tested it.

If you want to read a good introduction to the MOP, google for Andreas 
Paepcke and pick the second link. You can read the (defacto standard) 
specification for a MOP by googling for "CLOS MOP".


Pascal


-- 
Tyler: "How's that working out for you?"
Jack: "Great."
Tyler: "Keep it up, then."
From: Kenny Tilton
Subject: Re: change-class without losing mixins
Date: 
Message-ID: <9HCLc.55608$4h7.6659884@twister.nyc.rr.com>
Mark McConnell wrote:

> Say I have a class A1 that has a few slots.  I also have mixin classes
> B, C, D.  Each of these has a few slots, all distinct from the slots
> in the others and in A1.  Each of the classes "A1 with one mixin" has
> a name, like
> 
> (defclass A1-with-B (B A1)
>   ())
> 
> There may be classes like A1-with-BD or A1-with-BCD someday, but not
> right now.
> 
> There is also a subclass A2 of A1 that adds a few slots to A1.
> 
> Let x be an instance of some class that's A1 with one or more of B, C,
> D mixed in.  The problem is that, after a few minutes of computations,
> I discover that x should really belong to A2.  Its essential nature is
> A2-ness, but I had no way of knowing that until after the
> computations.
> 
> I want to call (change-class x 'A2) with appropriate initializations
> in an update-instance-for-different-class :after method.  However, my
> understanding from CLHS 7.2.* is that change-class will throw away the
> slots of B, C, and D, whichever were present.  I need x to retain
> those slots even as it becomes an A2.
> 
> I don't want to define A2-with-B, etc.  That's partly because it's too
> wordy.  But also, I don't know whether x will have 1 or 2 or 3 of the
> mixins; why define A2-with-BCD before I know whether A1-with-BCD will
> ever be needed?
> 
> Can you create a class precedence list on the fly?  Like an "anonymous
> class", the union of A2 and "whatever x originally satisfied".  I can
> imagine messy problems if A2's slots conflicted with BCD's slots, but
> they don't.
> 
> Any pointers to books or online references would be appreciated.

Damn, I was hoping you were going to ask if anyone thought this was an 
appropriate way to use OO. :)

Oh, well:

http://www.iit.nrc.ca/~martin/capabilities_web/docs/capabilities_home.html

hth, kenny

-- 
Cells? Cello? Celtik?: http://www.common-lisp.net/project/cells/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
From: Mark McConnell
Subject: Re: change-class without losing mixins
Date: 
Message-ID: <d3aed052.0407220544.133506cc@posting.google.com>
Kenny Tilton <·······@nyc.rr.com> wrote in message news:<·······················@twister.nyc.rr.com>...
> Mark McConnell wrote:
> 
> > Can you create a class precedence list on the fly?  Like an "anonymous
> > class", the union of A2 and "whatever x originally satisfied".  I can
> > imagine messy problems if A2's slots conflicted with BCD's slots, but
> > they don't.
> > 
> > Any pointers to books or online references would be appreciated.
> 
> Damn, I was hoping you were going to ask if anyone thought this was an 
> appropriate way to use OO. :)

Well, ok.  Does anyone think this is an appropriate way to use OO? 
Especially Kenny?  :-)

Pascal's post suggests it is an appropriate way to use meta-OO.

> 
> Oh, well:
> 
> http://www.iit.nrc.ca/~martin/capabilities_web/docs/capabilities_home.html
> 
> hth, kenny
From: Kenny Tilton
Subject: Re: change-class without losing mixins
Date: 
Message-ID: <blULc.97102$a92.49171@twister.nyc.rr.com>
Mark McConnell wrote:
> Kenny Tilton <·······@nyc.rr.com> wrote in message news:<·······················@twister.nyc.rr.com>...
> 
>>Mark McConnell wrote:
>>
>>
>>>Can you create a class precedence list on the fly?  Like an "anonymous
>>>class", the union of A2 and "whatever x originally satisfied".  I can
>>>imagine messy problems if A2's slots conflicted with BCD's slots, but
>>>they don't.
>>>
>>>Any pointers to books or online references would be appreciated.
>>
>>Damn, I was hoping you were going to ask if anyone thought this was an 
>>appropriate way to use OO. :)
> 
> 
> Well, ok.  Does anyone think this is an appropriate way to use OO? 
> Especially Kenny?  :-)

Let's just say the alarms are going off. Without knowing the functional 
objective of all these change-class shenanigans it is hard to be more 
specific. You are clearly going where CLOS does not want to go. Can the 
MOP pistol-whip it into complying? Non-portably, yes. But to what 
advantage over any of Lisp's zillion other more flexible ways of 
representing long-lived state? CLOS is cool, but I would not cling it to 
it for it's own sake.

> 
> Pascal's post suggests it is an appropriate way to use meta-OO.

There are two questions. The first is whether the OO shenanigans are 
justified to achieve the functional objective. The second is how to pull 
them off. That the MOP answers the second (non-portably) does not answer 
the first, or justify what might be a trickier solution than letting go 
of CLOS to achieve the functional objective at hand.

kt

-- 
Cells? Cello? Celtik?: http://www.common-lisp.net/project/cells/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
From: Mark McConnell
Subject: Re: change-class without losing mixins
Date: 
Message-ID: <d3aed052.0407230925.403eed34@posting.google.com>
Kenny Tilton <·······@nyc.rr.com> wrote in message news:<·····················@twister.nyc.rr.com>...
> Mark McConnell wrote:
> > Kenny Tilton <·······@nyc.rr.com> wrote in message news:<·······················@twister.nyc.rr.com>...
> > 
> >>Mark McConnell wrote:
> >>
> >>
> >>>Can you create a class precedence list on the fly?  Like an "anonymous
> >>>class", the union of A2 and "whatever x originally satisfied".  I can
> >>>imagine messy problems if A2's slots conflicted with BCD's slots, but
> >>>they don't.
> >>>
> >>>Any pointers to books or online references would be appreciated.
> >>
> >>Damn, I was hoping you were going to ask if anyone thought this was an 
> >>appropriate way to use OO. :)
> > 
> > 
> > Well, ok.  Does anyone think this is an appropriate way to use OO? 
> > Especially Kenny?  :-)
> 
> Let's just say the alarms are going off. Without knowing the functional 
> objective of all these change-class shenanigans it is hard to be more 
> specific. You are clearly going where CLOS does not want to go. Can the 
> MOP pistol-whip it into complying? Non-portably, yes. But to what 
> advantage over any of Lisp's zillion other more flexible ways of 
> representing long-lived state? CLOS is cool, but I would not cling it to 
> it for it's own sake.

Actually, I agree.  My alarms have been going off for a week.  CLOS
was working out well for other parts of this project.  But for this
particular part, it's time to find a non-CLOS solution.

I tend to keep within language standards (ANSI CL) to stay portable. 
So I'll avoid the MOP.

[philosophy] Most languages can create objects on the fly that satisfy
a contract of behaviors
(new javax.sound.midi.MidiDevice(){...}).  So why not classes on the
fly?  Why not ask the machine to make up sets of behaviors, to make up
contracts, before it creates the objects that satisfy those contracts?
 I guess the answer is, this is not OO, simply by definition of the
term.  It is meta-OO, but not OO.  OO is supposed to help a programmer
(or team of programmers) by making them register the contracts for
what they're building before they build it.  If the contracts are
being (re)written while the construction is going on, it's worse than
if the contracts were not written at all.  Because now the lawyers are
freaking out, as well as the builders.

For those who are interested, here is what I'm doing.  I'm writing a
package for finite group theory and representation theory.  (See
original post for notation.)  A1 = finite groups.  B, C, etc. are
"external" structural facts.  For instance, B might be quotient group:
if G is a group, then B holds pointers to groups H and K such that G =
H/K.  A2 is whether the group is abelian, i.e., whether xy = yx always
holds for x,y in G.  This is not external, but "internal".  This means
in practice that groups can be created in zillions of ways (quotient,
p-Sylow subgroup, maximal subgroup, maximal normal subgroup...) and
often you won't know if they're abelian unless you check by brute
force.  Yet most functions will need a special implementation if the
group is abelian.
From: Kenny Tilton
Subject: Re: change-class without losing mixins
Date: 
Message-ID: <b5fMc.98307$a92.60599@twister.nyc.rr.com>
Mark McConnell wrote:
> Kenny Tilton <·······@nyc.rr.com> wrote in message news:<·····················@twister.nyc.rr.com>...
> 
>>Mark McConnell wrote:
>>
>>>Kenny Tilton <·······@nyc.rr.com> wrote in message news:<·······················@twister.nyc.rr.com>...
>>>
>>>
>>>>Mark McConnell wrote:
>>>>
>>>>
>>>>
> [philosophy]

You might be unaware of Tilton's Law:

        There are no abstract problems.

Language features exist to solve problems and give otherwise useless 
academics something to...about which to write papers. Now if only I 
could understand a word of your stuff below on groups. :)

>> Most languages ...

Most?!!!!!!! 51%? I'll give you a break, name 10%.

can create objects on the fly that satisfy
> a contract of behaviors
> (new javax.sound.midi.MidiDevice(){...}).

That is so cute! First Sun tells us we do not need multiple inheritance 
and that static is fine, and then they let us dynamically mix in 
interfaces to make up for the fact that they were wrong both times.


   So why not classes on the
> fly?

You mis-read "non-portable" as "impossible". The MOP exists, it just 
ain't standard. Give the CL designers (and CL) credit: the MOP was 
great, but it was less than thirty years old. Sorry, Charlie, we cannot 
cast you in stone until you have proven the test of time. Extra credit: 
compare and contrast with the Perl/Python language du jour tossing the 
spec on each integral release.


   Why not ask the machine to make up sets of behaviors, to make up
> contracts, before it creates the objects that satisfy those contracts?
>  I guess the answer is, this is not OO, simply by definition of the
> term.  It is meta-OO, but not OO.

Yes and no. I would say it is "dynamic programming". Meta-OO simply 
means I have a way to tailor my own OO. It does not mean that my 
application does not know how to decompose its required capabilities 
according to a set of custom types.

Note that Lisp is all about meta-this and dynamic-that and that I am 
CLOS's biggest fan.

> For those who are interested, here is what I'm doing.  I'm writing a
> package for finite group theory and representation theory.  (See
> original post for notation.)  A1 = finite groups.  B, C, etc. are
> "external" structural facts.  For instance, B might be quotient group:
> if G is a group, then B holds pointers to groups H and K such that G =
> H/K.  A2 is whether the group is abelian, i.e., whether xy = yx always
> holds for x,y in G.  This is not external, but "internal".  This means
> in practice that groups can be created in zillions of ways (quotient,
> p-Sylow subgroup, maximal subgroup, maximal normal subgroup...) and
> often you won't know if they're abelian unless you check by brute
> force.  Yet most functions will need a special implementation if the
> group is abelian.

OK, I did not understand a word, but that has never stopped me before. 
btw, sounds like fun if you want to carry on and hammer this out.

What do you mean by "you won't know if they're abelian unless you check 
by brute force"? I was thinking a group was an abstract category with 
membership rules. This sounds like a group is a population of actual 
instances and we deduce a rule when all instances satisfy it. Weird.

Anyway, I trust you that the combinations (yes? not permutations?) are 
astronomical. But there are only so many (however many) categories such 
as abelian, yes? And for each, there are a finite number of places where 
they matter.

What if you just tracked the attributes (abelian,wtc) of a group as so 
many symbols? GFs can be dispatched on eql parameters (in any compliant 
CL). Without getting into the MOP, you might be able to roll your own 
dispatch by having a function XYZ call XYZ-GF for which specific methods 
can be coded as necessary, applied to each symbol in a groups atrribute 
list (the default bevaior is an NOP).

This is an "interesting" problem, in ways in which CLOS (which I swear 
by) is neither interesting, efficient, sufficent, nor necessary. Lose it. :)

kt

-- 
Cells? Cello? Celtik?: http://www.common-lisp.net/project/cells/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
From: Mark McConnell
Subject: Re: change-class without losing mixins
Date: 
Message-ID: <d3aed052.0407251239.be74d90@posting.google.com>
Kenny Tilton <·······@nyc.rr.com> wrote in message news:<·····················@twister.nyc.rr.com>...
> btw, sounds like fun if you want to carry on and hammer this out.

Thanks, Kenny.  I wrote a working version of the package last
spring-summer-fall in Java.  The class/subclass structure there was a
simplified version of what's described in the original post.  These
days I'm translating the package to Lisp, because, well, we know how
we feel about the language.
From: Rahul Jain
Subject: Re: change-class without losing mixins
Date: 
Message-ID: <87wu0ti2vz.fsf@nyct.net>
···············@yahoo.com (Mark McConnell) writes:

> For those who are interested, here is what I'm doing.  I'm writing a
> package for finite group theory and representation theory.  (See
> original post for notation.)  A1 = finite groups.  B, C, etc. are
> "external" structural facts.  For instance, B might be quotient group:
> if G is a group, then B holds pointers to groups H and K such that G =
> H/K.  A2 is whether the group is abelian, i.e., whether xy = yx always
> holds for x,y in G.  This is not external, but "internal".  This means
> in practice that groups can be created in zillions of ways (quotient,
> p-Sylow subgroup, maximal subgroup, maximal normal subgroup...) and
> often you won't know if they're abelian unless you check by brute
> force.  Yet most functions will need a special implementation if the
> group is abelian.

But the group's properties won't change through time if you make a
group's definition immutable. What you need is a cache of all the mixed
group types you've already created so that you don't have to keep
creating a new anonymous class for each group.

Define a MAKE-GROUP function that will go and analyze the properties of
a group and find/create the appropriate class as needed, then
instantiate a group of that type with the elements and operators given. 
You can even have the mixins registered in some way along with a way of
testing a group for those properties. Also, some way for operators
(division, maximal subgroup, etc) to optimize the testing of those
properties by applying rules based on the types of the groups involved
in the operation.

-- 
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist
From: Mark McConnell
Subject: Re: change-class without losing mixins
Date: 
Message-ID: <d3aed052.0407251301.4a18f7eb@posting.google.com>
Rahul Jain <·····@nyct.net> wrote in message news:<··············@nyct.net>...
> ···············@yahoo.com (Mark McConnell) writes:
> 
> > For those who are interested, here is what I'm doing.  I'm writing a
> > package for finite group theory and representation theory.
[snip]

> 
> But the group's properties won't change through time if you make a
> group's definition immutable. 

That's right, I keep the groups immutable.

> What you need is a cache of all the mixed
> group types you've already created so that you don't have to keep
> creating a new anonymous class for each group.
> 
> Define a MAKE-GROUP function that will go and analyze the properties of
> a group and find/create the appropriate class as needed

How would you create these classes?  With the MOP (as in Pascal's post
near the top)?

> instantiate a group of that type with the elements and operators given. 

> You can even have the mixins registered in some way along with a way of
> testing a group for those properties. Also, some way for operators
> (division, maximal subgroup, etc) to optimize the testing of those
> properties by applying rules based on the types of the groups involved
> in the operation.

This sounds like the kind of pre-CLOS OO design you see in books. 
Make a table or matrix where the x-axis has the functions you want to
call, the y-axis has the set of types, and the xy-cells contain
methods.  I have definitely thought about this approach (and have used
it in big projects before).  In my current project, you know some
"external" properties of a group at creation time (is it a direct
product, etc.)  Other "internal" ones you have to work out as you go
along (abelian?  simple?)  My current plan is to name the external
properties with classes and let CLOS handle them, and to deal with the
internal ones by sticking them into (an) extra slot(s) in each group.
From: Rahul Jain
Subject: Re: change-class without losing mixins
Date: 
Message-ID: <87hdrvsp2x.fsf@nyct.net>
···············@yahoo.com (Mark McConnell) writes:

>> Define a MAKE-GROUP function that will go and analyze the properties of
>> a group and find/create the appropriate class as needed
>
> How would you create these classes?  With the MOP (as in Pascal's post
> near the top)?

I suppose that the initargs of standard-class are not specified in the
CL spec, so yes, that would be considered MOP. But it's not really
"intensive" MOP.

>> instantiate a group of that type with the elements and operators given. 
>
>> You can even have the mixins registered in some way along with a way of
>> testing a group for those properties. Also, some way for operators
>> (division, maximal subgroup, etc) to optimize the testing of those
>> properties by applying rules based on the types of the groups involved
>> in the operation.
>
> This sounds like the kind of pre-CLOS OO design you see in books. 
> Make a table or matrix where the x-axis has the functions you want to
> call, the y-axis has the set of types, and the xy-cells contain
> methods.

If you write the code such that the xy-cells can be defined
independently, then you are using CLOS.

> I have definitely thought about this approach (and have used
> it in big projects before).  In my current project, you know some
> "external" properties of a group at creation time (is it a direct
> product, etc.)  Other "internal" ones you have to work out as you go
> along (abelian?  simple?)  My current plan is to name the external
> properties with classes and let CLOS handle them, and to deal with the
> internal ones by sticking them into (an) extra slot(s) in each group.

I think you'll be able to actually compute the results of various
operations faster, or at least more simply in code, if you let the
internal properties be classes as well. You just need to have an extra
level of indirection just before actually instantiating the group so
that these mixins can be computed up front.

Or you could go ahead and use change-class. I don't think it's a bad
idea in this case. You'd just need a registry of the various classes
that can be indexed in an order-independent way (or define a natural
ordering of the classes -- this can be done dynamically at runtime by
automatically associating a serial number with each of the classes as
they are defined). You'd need this kind of data structure anyway with
the MAKE-GROUP solution that I proposed.

-- 
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist
From: Thomas A. Russ
Subject: Re: change-class without losing mixins
Date: 
Message-ID: <ymihdrutqfz.fsf@sevak.isi.edu>
···············@yahoo.com (Mark McConnell) writes:

> > > For those who are interested, here is what I'm doing.  I'm writing a
> > > package for finite group theory and representation theory.

Hmmm.  I seem to have missed some of these messages, but it seems that
what you are doing involves trying to define objects, where part of
their definition depends on the properties they have.  This is a type of
reasoning that is not particularly well supported by CLOS or other
object-oriented paradigms.

It does, however, fit the model of so-called Description Logics.  Since
you are doing this in Common Lisp, I would suggest you take a look at
our Loom system:

   http://www.isi.edu/isd/LOOM/

The Loom language will allow the construction of definitions where the
presence of relations (i.e., slot values) can determine the
classification and type of an object.  It also allows instances to be
assigned multiple types, unlike object-oriented systems.  Instead of
trying to use change-class, you just assert additional types and then
get a (dynamically created) amalgamation of the types.

-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Rahul Jain
Subject: Re: change-class without losing mixins
Date: 
Message-ID: <87u0vjpz0f.fsf@nyct.net>
···@sevak.isi.edu (Thomas A. Russ) writes:

> ···············@yahoo.com (Mark McConnell) writes:
>
>> > > For those who are interested, here is what I'm doing.  I'm writing a
>> > > package for finite group theory and representation theory.
>
> Hmmm.  I seem to have missed some of these messages, but it seems that
> what you are doing involves trying to define objects, where part of
> their definition depends on the properties they have.  This is a type of
> reasoning that is not particularly well supported by CLOS or other
> object-oriented paradigms.

Oof, I forgot about Weyl, part of Simlab. It's an abstract algebra
library which you can use to actually manipulate groups, etc. You might
want to take a look at it, Mark.

-- 
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist
From: Rahul Jain
Subject: Re: change-class without losing mixins
Date: 
Message-ID: <87llh7ssyu.fsf@nyct.net>
···············@yahoo.com (Mark McConnell) writes:

> Most languages can create objects on the fly that satisfy
> a contract of behaviors
> (new javax.sound.midi.MidiDevice(){...}).

Just to clarify something: Java does _not_ allow code like that to
create a new contract of behaviors. It just allows you to implement that
contract differently for a specific location where you instantiate it. 
Doing otherwise would violate Java's static typing. Also, it's
impossible to cast an object to an anonymous subclass because, well,
it's anonymous.

You can unbunch your panties now, Kenny. :)

-- 
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist
From: Pascal Costanza
Subject: Re: change-class without losing mixins
Date: 
Message-ID: <cdr2ih$t1k$1@f1node01.rhrz.uni-bonn.de>
Kenny Tilton wrote:

>> Pascal's post suggests it is an appropriate way to use meta-OO.
> 
> There are two questions. The first is whether the OO shenanigans are 
> justified to achieve the functional objective. The second is how to pull 
> them off. That the MOP answers the second (non-portably) does not answer 
> the first, or justify what might be a trickier solution than letting go 
> of CLOS to achieve the functional objective at hand.

Kenny is right wrt to change-class. The OP has asked a technical 
question wrt CLOS and I have only answered the technical part.

change-class is dangerous because it potentially breaks invariants in 
other parts of your program in a bad way. For example, assume the 
following code.

(defclass C () ())

(defclass D (C) ())

(defmethod test ((o C))
   (do-something o)
   (proceed ...))

(defmethod test ((o D))
   (do-something-else o)
   (proceed-differently ...))

(defun do-something (o)
   (change-class o 'D))

After change-class has been performed on a C instance, the call site 
will proceed, but it won't proceed-differently as one might expect. 
Things get even worse if you add this:

(defun do-something-else (o)
   (change-class o 'C))

Now code will be executed under the assumption that an object is of 
class D whereas it is in fact of the ("lesser") class C.

So it's probably better to look for a different solution before you 
settle on someting that depends on change-class.


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: Alan Crowe
Subject: Re: change-class without losing mixins
Date: 
Message-ID: <863c3jxbil.fsf@cawtech.freeserve.co.uk>
Mark McConnell hits a snag:
> I want to call (change-class x 'A2) with appropriate
> initializations in an update-instance-for-different-class
> :after method.  However, my understanding from CLHS 7.2.*
> is that change-class will throw away the slots of B, C,
> and D, whichever were present.  I need x to retain those
> slots even as it becomes an A2.

There is a basic issue about making use of CLOS that I have
yet to get any feel for:

   How do you choose between

     1)Inheriting from another class

     2)Adding a slot containing an instance of the other
       class

First way:

(defclass thing()
    ((stuff :accessor stuff :initarg :stuff)))

(defclass with-knobs-on()
    ((knob :accessor knob :initarg :knob)))

(defclass thing-with-knobs-on(thing with-knobs-on)
    nil)

(defclass super-thing(thing)
    ((more-stuff :accessor extra :initarg :plus)))

(defmethod update-instance-for-different-class :after
    ((old thing)(new super-thing) &key add)
    (setf (extra new)
	  add))

CL-USER(35): (defparameter obj
	       (make-instance
			    'thing-with-knobs-on
			    :stuff 'pediment
			    :knob 'finial))
OBJ
CL-USER(36): (describe obj)
#<THING-WITH-KNOBS-ON @ #x1049f67a> is an
    instance of
    #<STANDARD-CLASS THING-WITH-KNOBS-ON>:
 The following slots have :INSTANCE allocation:
  KNOB    FINIAL
  STUFF   PEDIMENT
CL-USER(37): (change-class obj 'super-thing
			   :add 'balustrade)
#<SUPER-THING @ #x1049f67a>
CL-USER(38): (describe obj)
#<SUPER-THING @ #x1049f67a> is an instance of
    #<STANDARD-CLASS SUPER-THING>:
 The following slots have :INSTANCE allocation:
  STUFF        PEDIMENT
  MORE-STUFF   BALUSTRADE

Oh no! I've lost my finial.

Second way 

(defclass thing()
  ((stuff :accessor stuff :initarg :stuff)
   (expansion-slot :accessor additional
		   :initarg plus
		   :initform nil)))

(defclass with-knobs-on()
    ((knob :accessor knob :initarg :knob)))

(defclass with-bells-on()
  ((bell :accessor ring :initarg :bell)))

(defun build-thing(&key stuff (knob nil) (bell nil))
  (let ((temp (make-instance 'thing :stuff stuff)))
    (when knob
      (push (make-instance 'with-knobs-on :knob knob)
	    (additional temp)))
    (when bell
      (push (make-instance 'with-bells-on :bell bell)
	    (additional temp)))
    temp))

(defclass super-thing(thing)
    ((more-stuff :accessor extra :initarg :plus)))
      
(defmethod update-instance-for-different-class :after
    ((old thing)(new super-thing) &key add)
    (setf (extra new)
	  add))

(defmethod knob ((item thing))
  (knob (find-if (lambda(x)(typep x 'with-knobs-on))
		 (additional item))))

(defmethod (setf knob)(value (item thing))
  (setf (knob (find-if (lambda(x)(typep x 'with-knobs-on))
		 (additional item)))
	value))

CL-USER(2): (defvar obj (build-thing :stuff 'gable
				      :knob 'finial))
OBJ
CL-USER(3): (change-class obj 'super-thing :add 'frieze)
#<SUPER-THING @ #x10493e2a>
CL-USER(4): (knob obj)
FINIAL
CL-USER(5): (setf (knob obj) 'globe)
GLOBE

Now SUPER-THING	inherits expandability from THING.

I think I've blundered with using a list in the expansion
slot. It should be a single object, which gets subclassed
and change-classed to provide flexibility in THING. That
might require more skill with allow-other-keys than I
currently possess.

Alan Crowe
Edinburgh
Scotland
From: Mark McConnell
Subject: Re: change-class without losing mixins
Date: 
Message-ID: <d3aed052.0407230936.adc1fc5@posting.google.com>
Alan Crowe <····@cawtech.freeserve.co.uk> wrote in message news:<··············@cawtech.freeserve.co.uk>...
> There is a basic issue about making use of CLOS that I have
> yet to get any feel for:
> 
>    How do you choose between
> 
>      1)Inheriting from another class
> 
>      2)Adding a slot containing an instance of the other
>        class
> [snip]
> Second way 
> 
> (defclass thing()
>   ((stuff :accessor stuff :initarg :stuff)
>    (expansion-slot :accessor additional
> 		   :initarg plus
> 		   :initform nil)))

I'll probably take an approach like this one.

[snip]

> (defmethod knob ((item thing))
>   (knob (find-if (lambda(x)(typep x 'with-knobs-on))
> 		 (additional item))))

There's a question here: what does the method for knob do if the
expansion-slot has no with-knobs-on object?  You'll end up with a call
to
(knob nil).  It will have to be handled, but that's easy.

> 
> (defmethod (setf knob)(value (item thing))
>   (setf (knob (find-if (lambda(x)(typep x 'with-knobs-on))
> 		 (additional item)))
> 	value))

I haven't checked, but I'm not sure this will work.  How about
something with remove-if followed by push?
From: Joe Marshall
Subject: Re: change-class without losing mixins
Date: 
Message-ID: <n01q4sl0.fsf@ccs.neu.edu>
Alan Crowe <····@cawtech.freeserve.co.uk> writes:

> There is a basic issue about making use of CLOS that I have
> yet to get any feel for:
>
>    How do you choose between
>
>      1)Inheriting from another class
>
>      2)Adding a slot containing an instance of the other
>        class

You use inheritance when there is an `is-a' relationship between the
classes:  a car is a vehicle, a cat is a mammal.  All operations on
the superclass make sense on the subclass.  Vehicles can move, so cars
can move.  You can shave a mammal, so you can shave a cat.  Every car
is a vehicle, every cat is a mammal.

You use slots for containing things.  A car may have wheels, but a car
is not a wheel.  A cat may have whiskers, but a cat is not a whisker.
Operations on wheels don't make sense on cars.  You wouldn't retread a
car.

> First way:
>
> (defclass thing()
>     ((stuff :accessor stuff :initarg :stuff)))
>
> (defclass with-knobs-on()
>     ((knob :accessor knob :initarg :knob)))
>
> (defclass thing-with-knobs-on(thing with-knobs-on)
>     nil)
>
> (defclass super-thing(thing)
>     ((more-stuff :accessor extra :initarg :plus)))

Careful with this name, super-thing is a subclass of thing.

>
> (defmethod update-instance-for-different-class :after
>     ((old thing)(new super-thing) &key add)
>     (setf (extra new)
> 	  add))
>
> CL-USER(35): (defparameter obj
> 	       (make-instance
> 			    'thing-with-knobs-on
> 			    :stuff 'pediment
> 			    :knob 'finial))
> OBJ
> CL-USER(36): (describe obj)
> #<THING-WITH-KNOBS-ON @ #x1049f67a> is an
>     instance of
>     #<STANDARD-CLASS THING-WITH-KNOBS-ON>:
>  The following slots have :INSTANCE allocation:
>   KNOB    FINIAL
>   STUFF   PEDIMENT
> CL-USER(37): (change-class obj 'super-thing
> 			   :add 'balustrade)
> #<SUPER-THING @ #x1049f67a>
> CL-USER(38): (describe obj)
> #<SUPER-THING @ #x1049f67a> is an instance of
>     #<STANDARD-CLASS SUPER-THING>:
>  The following slots have :INSTANCE allocation:
>   STUFF        PEDIMENT
>   MORE-STUFF   BALUSTRADE
>
> Oh no! I've lost my finial.

A super-thing is not a `with-knobs-on'.

> Second way 
>
> (defclass thing()
>   ((stuff :accessor stuff :initarg :stuff)
>    (expansion-slot :accessor additional
> 		   :initarg plus
> 		   :initform nil)))
>
> (defclass with-knobs-on()
>     ((knob :accessor knob :initarg :knob)))
>
> (defclass with-bells-on()
>   ((bell :accessor ring :initarg :bell)))
>
> (defun build-thing(&key stuff (knob nil) (bell nil))
>   (let ((temp (make-instance 'thing :stuff stuff)))
>     (when knob
>       (push (make-instance 'with-knobs-on :knob knob)
> 	    (additional temp)))
>     (when bell
>       (push (make-instance 'with-bells-on :bell bell)
> 	    (additional temp)))
>     temp))
>
> (defclass super-thing(thing)
>     ((more-stuff :accessor extra :initarg :plus)))
>       
> (defmethod update-instance-for-different-class :after
>     ((old thing)(new super-thing) &key add)
>     (setf (extra new)
> 	  add))
>
> (defmethod knob ((item thing))
>   (knob (find-if (lambda(x)(typep x 'with-knobs-on))
> 		 (additional item))))
>
> (defmethod (setf knob)(value (item thing))
>   (setf (knob (find-if (lambda(x)(typep x 'with-knobs-on))
> 		 (additional item)))
> 	value))
>
> CL-USER(2): (defvar obj (build-thing :stuff 'gable
> 				      :knob 'finial))
> OBJ
> CL-USER(3): (change-class obj 'super-thing :add 'frieze)
> #<SUPER-THING @ #x10493e2a>
> CL-USER(4): (knob obj)
> FINIAL
> CL-USER(5): (setf (knob obj) 'globe)
> GLOBE
>
> Now SUPER-THING	inherits expandability from THING.
>
> I think I've blundered with using a list in the expansion
> slot. It should be a single object, which gets subclassed
> and change-classed to provide flexibility in THING. That
> might require more skill with allow-other-keys than I
> currently possess.

Don't go overboard with the CLOS paradigm.  It is fine to have a list
of miscellaneous objects in an expansion slot if you need to.  Many
objects support a `property-list' mixin for this reason.
From: Pascal Costanza
Subject: Re: change-class without losing mixins
Date: 
Message-ID: <cdr1s4$t1g$1@f1node01.rhrz.uni-bonn.de>
Alan Crowe wrote:

> There is a basic issue about making use of CLOS that I have
> yet to get any feel for:
> 
>    How do you choose between
> 
>      1)Inheriting from another class
> 
>      2)Adding a slot containing an instance of the other
>        class

This is a beginner's problem in any OOP language. The general rule of 
thumb is this: Are you dealing with an is-a relationship or with a has-a 
relationship. In your example, it's always a has-a relationship.

Secondly, what you seem to want is a way to deal with optional slots. 
First of all, if the number of slots is small it's already possible in 
CLOS to have unbound slots. It's straightforward to interpret an unbound 
slot as one that's currently not available. If the number of optional 
slots is high, what's wrong with storing the slots in a hash table? It's 
even possible to seamlessly work with those slots by overriding 
slot-missing, so that you can still call slot-value for those slots. The 
only drawback may be that you have to explicitly write your own accessor 
functions, but so what?


Pascal

-- 
Pascal Costanza               University of Bonn
···············@web.de        Institute of Computer Science III
http://www.pascalcostanza.de  R�merstr. 164, D-53117 Bonn (Germany)