[comp.object] prototypes and degenerate cases

brucec@phoebus.phoebus.labs.tek.com (Bruce Cohen;;50-662;LP=A;) (09/12/90)

I've just been reading the Self papers, and it occurred to me that using
prototypes for inheritance could solve a problem which has been annoying me
for quite some time.  Maybe some of you out there coould comment on the
theory and/or practice of the idea.

Consider an object-oriented graphic drawing system, in which there's a
general shape object, a polygon object, a rectangle object, a circle
object, and an ellipse object.  One way you could arrange class inheritance
for such objects is like this:

                                   Shape
                                     |
                                ------------
                                |          |
                            Polygon     Ellipse
                                |          |
                            Rectangle   Circle


That is, the root class is an abstract shape class, and it's subclasses are
the most general concrete classes, with more specific (i.e., degenerate case)
classes inheriting from them.  Well that's fine from a static viewpoint,
but consider that one reason to build a system like this is that it may
be (and usually is) cheaper both to draw and to store a rectangle than a
general polygon, and a circle than an ellipse.  OK, so we can create
rectangles when we know that a polygon has 4 sides, or circles when we know
that the foci of an ellipse are coincident.

But what happens when objects can change over time?  How do we dynamically
mutate a rectangle to a polygon and back again while retaining the cost
savings of the more refined object?  In a class-based system, either you
use a message like become: otherObject in Smalltalk, which is really a
system utility, and implies that there is a global object table or some
other centralized means of insuring that references to the old object get
update, or you have to have creation/conversion methods for objects which
can be called when a mutator method discovers a degeneracy (this is the
technique you would have to use in C++).  The mutator would create (e.g.) a
new degenerate object, copy the relevant part of the state of the
non-degenerate one, then install the new one in the right place and
manually update references to the old object.

Suppose instead that the objects inherited from prototypes, and the mutator
merely changed the appropriate parent pointers to (e.g.) remove a
degenerate object's redundant slots and change it's behavior to the less
costly method.  Consider the ellipse being converted to a circle (warning:
Self notation):

Ellipse                                 Circle:
------                                  -------
parent:-----------> traits circle       parent:-----------> traits ellipse
focus1 -----------> 0@0   <------------ center
focus2 -----------> 0@0
focus1: <-
focus2: <-                              focus2: <-
major radius -----> 1     <------------ radius
minor radius -----> 1
major radius: <-
minor radius: <-

This seems significantly cleaner than becomes, and perhaps less work than
the hand-crafted mutator in the class-based approach.  Anyone have any
comments?

--
---------------------------------------------------------------------------
NOTE: USE THIS ADDRESS TO REPLY, REPLY-TO IN HEADER MAY BE BROKEN!
Bruce Cohen, Computer Research Lab        email: brucec@tekcrl.labs.tek.com
Tektronix Laboratories, Tektronix, Inc.                phone: (503)627-5241
M/S 50-662, P.O. Box 500, Beaverton, OR  97077

boissier@irisa.irisa.fr (franck boissiere,externes ) (09/13/90)

From article <BRUCEC.90Sep11141457@phoebus.phoebus.labs.tek.com>, by brucec@phoebus.phoebus.labs.tek.com (Bruce Cohen;;50-662;LP=A;):
> I've just been reading the Self papers, and it occurred to me that using
> prototypes for inheritance could solve a problem which has been annoying me
> for quite some time.  Maybe some of you out there coould comment on the
> theory and/or practice of the idea.
> 
> 
> Ellipse                                 Circle:
> ------                                  -------
> parent:-----------> traits circle       parent:-----------> traits ellipse
> focus1 -----------> 0@0   <------------ center
> focus2 -----------> 0@0
> focus1: <-
> focus2: <-                              focus2: <-
> major radius -----> 1     <------------ radius
> minor radius -----> 1
> major radius: <-
> minor radius: <-
> 
> This seems significantly cleaner than becomes, and perhaps less work than
> the hand-crafted mutator in the class-based approach.  Anyone have any
> comments?
> 
> --

It is not always needed nor desirable to have object mutating in a system,
but this is not exactly the point you make. Object coercion is implemented
without invoking becomes: and I feel this is what you need. I don't really
see what your example in Self does better compared to the "asSUCH" methods

of Smalltalk. I may be missing something.
-- 
Franck BOISSIERE                        boissier@irisa.irisa.fr
Prototyping Lab Manager                 boissier@ccettix.UUCP
C.C.E.T.T.   B.P. 59                    boissier%irisa.irisa.fr@uunet.uu.net
35512 CESSON SEVIGNE CEDEX  FRANCE    

pcg@cs.aber.ac.uk (Piercarlo Grandi) (09/13/90)

On 11 Sep 90 21:14:57 GMT, brucec@phoebus.phoebus.labs.tek.com (Bruce
Cohen) said:

brucec> I've just been reading the Self papers, and it occurred to me
brucec> that using prototypes for inheritance could solve a problem
brucec> which has been annoying me for quite some time.  Maybe some of
brucec> you out there coould comment on the theory and/or practice of
brucec> the idea.

brucec> Consider an object-oriented graphic drawing system, in which
brucec> there's a general shape object, a polygon object, a rectangle
brucec> object, a circle object, and an ellipse object.  One way you
brucec> could arrange class inheritance for such objects is like this:

	[ ... describing the problem where an object's class semantics
	are a derivative of its parent's, but is implementation need
	not be ... ]

This is the age old problem, which I never tire to describe, that you
want to reuse interface, semantics, implementation separately. In your
case you want to reuse the interface and semantics of Shape for Polygon,
and again for Rectangle, but you don't want to reuse the implementation
of Polygon for Rectangle.

	[ ... describing substituing an object implementation for
	another, either with an in-built become:, or with a manually
	implemented version ... ]

Your two alternatives, incidentally, are exactly the same; when you
reject become:, you propose an alternative that is equivalent to
manually implementing become: itself.

brucec> Suppose instead that the objects inherited from prototypes, and
brucec> the mutator merely changed the appropriate parent pointers to
brucec> (e.g.) remove a degenerate object's redundant slots and change
brucec> it's behavior to the less costly method.  Consider the ellipse
brucec> being converted to a circle (warning: Self notation):

brucec> Ellipse                                 Circle:
brucec> ------                                  -------
brucec> parent:-----------> traits circle       parent:-------> traits ellipse
brucec> focus1 -----------> 0@0   <------------ center
brucec> focus2 -----------> 0@0
brucec> focus1: <-
brucec> focus2: <-                              focus2: <-
brucec> major radius -----> 1     <------------ radius
brucec> minor radius -----> 1
brucec> major radius: <-
brucec> minor radius: <-

This is OK -- what you do is to redefine inheritance so that you inherit
protocol but not implementation. This can be done in CLOS, as well.

brucec> This seems significantly cleaner than becomes, and perhaps less
brucec> work than the hand-crafted mutator in the class-based approach.
brucec> Anyone have any comments?

If done at run time it requires an interpreter or a runtime compiler,
because you are changing dynamically the layout in memory of slots and
the access algorithms to them. This may be available for Common Lisp and
Self, but not for simpler languages. And it is not that different from
become: really; it is an in-place version of become:, where you also
twiddle the parent pointer.

Bah. I think that become: is clean enough, can be implemented easily,
and is more flexible. It merely requires runtime diaptching of methods,
not runtime compilation.
--
Piercarlo "Peter" Grandi           | ARPA: pcg%uk.ac.aber.cs@nsfnet-relay.ac.uk
Dept of CS, UCW Aberystwyth        | UUCP: ...!mcsun!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk

craig@Neon.Stanford.EDU (Craig D. Chambers) (09/14/90)

I think there's some confusion about dynamic inheritance in Self.  In
the circle-mutating-into-ellipse example, there would be a *child*
object of either a circle object or an ellipse object, and other
objects would point to this child.  This child would act just like
either a circle or an ellipse, because of normal object inheritance
(the state and behavior of the parent is inherited, and accessible
transparently to clients who can't tell there's an extra child object
serving as mediator).

  Before:

	      circle
		A
		|
		|
      ----->  child

  change child's parent to turn into an ellipse

  After:

	     ellipse
		A
		|
		|
      ----->  child


To mutate a circle into an ellipse, a new ellipse object would be
created with the appropriate state, and the parent of the child object
would be redirected to the ellipse object from the circle object.  All
clients still point to the child, and so the child appears to have
"become" an ellipse (or at least its external behavior has changed so
that its two foci are no longer the same point).  But no weird
operations (like mutating the formats of objects or redirecting all
external pointers) are happening, just a single assignment to a single
(parent) slot.  Dynamic inheritance is *much* more structured and
clean than become:.

We use dynamic inheritance in a structured way in our Self
programming.  One common example occurs when an object may be in one
of several "behavior modes."  The canonical example is a window that
may be displayed either open or iconified.  The window object has an
assignable parent pointer that either inherits from normal window
behavior or from icon behavior, and can shift from one to the other
dynamically as the user iconifies and un-iconifies his windows.  The
resulting code is simpler to understand and extend since the open and
iconified cases are separated (just as object-oriented programming
improved procedural programming by factoring out different cases into
separate routines selected using dynamic method dispatching).

Another use for dynamic inheritance is to implement self-reorganizing
data structures.  For example, a table might be implemented as a list
of associations if it's small, but as it grows it might choose to
change itself into a hash table for better performance.  This could be
implemented most easily by having the user's handle on the collection
be a child of either a list object or a hash table object, depending
on the representation.  The circle-vs-ellipse example is similar in
flavor.  This class of example is useful where the interface to the
object doesn't change, but its internal representation and internal
algorithms do change.

To sum up, being able to support different representations of the same
abstraction (such as polygons vs. rectangles vs. squares) is
important, and some languages (like Self, Trellis/Owl, and probably
CLOS but not like Smalltalk, C++, or Eiffel) support subclasses whose
representation is different from their superclasses'.  But it is also
important to allow objects to change their implementation or
inheritance dynamically to best implement their current behavior.
This is an implementation decision that is perfectly reasonable in
supporting the object's interface.  This is where languages with
object inheritance that can change at run-time provide additional
"clean" power over class-based languages, whose inheritance patterns
are static (in every language I know about) and can only solve this
problem using hacks like become: or changing an instance's class.

-- Craig Chambers

jml@wally.altair.fr (Jean Marie Larcheveque) (09/14/90)

In article <1990Sep13.235832.23454@Neon.Stanford.EDU> craig@Neon.Stanford.EDU (Craig D. Chambers) writes:
>
>I think there's some confusion about dynamic inheritance in Self.  In
>the circle-mutating-into-ellipse example, there would be a *child*
>object of either a circle object or an ellipse object, and other
>objects would point to this child.  This child would act just like
>either a circle or an ellipse, because of normal object inheritance
>(the state and behavior of the parent is inherited, and accessible
>transparently to clients who can't tell there's an extra child object
>serving as mediator).

The decisive decision here is not the use of dynamic
inheritance, but the creation of an extra link (an empty
child, inheriting behavior *and* state) to avoid having to
update external links when the representation changes.
The same scheme could be implemented for example in C++,
with something like

  class ellipse {
	concrete_ellipse state_holder;
	// ...
  }
with concrete_ellipse a subclass of ellipse and a
superclass of circle.

--
Jean-Marie Larcheveque  <jml@bdblues.altair.fr> or <jml@nuri.inria.fr>

brucec@phoebus.phoebus.labs.tek.com (Bruce Cohen;;50-662;LP=A;) (09/15/90)

In article <1608@seti.inria.fr> jml@wally.altair.fr (Jean Marie Larcheveque) writes:
> 
> In article <1990Sep13.235832.23454@Neon.Stanford.EDU> craig@Neon.Stanford.EDU (Craig D. Chambers) writes:
>>
> The same scheme could be implemented for example in C++,
> with something like
> 
>   class ellipse {
>     concrete_ellipse state_holder;
>     // ...
>   }
> with concrete_ellipse a subclass of ellipse and a
> superclass of circle.
> 

But then don't you have to add a set of delegation functions to ellipse so
an instance of it can pass on calls to it's instance of concrete_ellipse?

Also (a nit), I think that state_holder has to be a pointer to
concrete_ellipse, because, concrete_ellipse being subclass of ellipse,
there's no way to forward-reference its type for the layout of the
structure to be determined.
--
---------------------------------------------------------------------------
NOTE: USE THIS ADDRESS TO REPLY, REPLY-TO IN HEADER MAY BE BROKEN!
Bruce Cohen, Computer Research Lab        email: brucec@tekcrl.labs.tek.com
Tektronix Laboratories, Tektronix, Inc.                phone: (503)627-5241
M/S 50-662, P.O. Box 500, Beaverton, OR  97077

sakkinen@tukki.jyu.fi (Markku Sakkinen) (09/16/90)

In article <BRUCEC.90Sep11141457@phoebus.phoebus.labs.tek.com>
brucec@phoebus.phoebus.labs.tek.com (Bruce Cohen;;50-662;LP=A;) writes:
>I've just been reading the Self papers, and it occurred to me that using
>prototypes for inheritance could solve a problem which has been annoying me
>for quite some time.  Maybe some of you out there coould comment on the
>theory and/or practice of the idea.

I don't think that the core of your problem is dependent on whether
the object model is based on classes or prototypes.
I'll try to explain why.

>Consider an object-oriented graphic drawing system, in which there's a
>general shape object, a polygon object, a rectangle object, a circle
>object, and an ellipse object.  One way you could arrange class inheritance
>for such objects is like this:

>                                   Shape
>                                     |
>                                ------------
>                                |          |
>                            Polygon     Ellipse
>                                |          |
>                            Rectangle   Circle


>That is, the root class is an abstract shape class, and it's subclasses are
>the most general concrete classes, with more specific (i.e., degenerate case)
>classes inheriting from them. [...]
> ...

>But what happens when objects can change over time?  How do we dynamically
>mutate a rectangle to a polygon and back again while retaining the cost
>savings of the more refined object? [...]
> [...](this is the
>technique you would have to use in C++).  The mutator would create (e.g.) a
>new degenerate object, copy the relevant part of the state of the
>non-degenerate one, then install the new one in the right place and
>manually update references to the old object.

Unfortunately this idea does not quite work at least in C++:
there is no way to find all references and pointers to a given object.
A more fundamental problem is that inheritance in almost all OOPL's
binds together the interface and the implementation of classes.
This problem has often been discussed in the literature (e.g. by
Alan Snyder). If Circle is defined as a subclass of Ellipse,
then every Circle object must contain at least all instance variables
of Ellipse, although it would actually need less.

Note that Circle is not strictly a subtype of Ellipse
but only a "read-only subtype": we can modify the axis ratio of
an ellipse, but if we try to do that to a circle,
it ceases to be a circle.

There are two main ways to look at the subclasses of each most general
concrete class: either we want them to be very visible to clients
and have an enhanced interface (e.g. additional operations applicable
to rectangles but not to other polygons), or we want them to be merely
more efficient implementations of special cases and have exactly the
same interface as their superclasses.
It seems from the preceding paragraph of the original posting
that your goal is the latter, so I'll first propose a solution to that.
(In fact I'll postpone the discussion of the former case to a later
posting, to keep this one at a reasonable size.)

Advertise only the classes Polygon and Ellipse to potential clients,
but define for them perhaps only one instance variable,
which will simply reference an object of an "implementation class"
that is utilised behind the scenes. All operations requested by clients
from a Polygon or Circle object are simply forwarded to the
implementation object.
For the small example given we need as implementation classes:
GeneralPolygon, Rectangle, GeneralEllipse, Circle.
These shall _not_ be subclasses of the "front end" classes Polygon
and Ellipse, but it could very well be expedient to make GeneralEllipse
a subclass of Circle! (Yes, the inheritance hierarchy of implementation
classes often gets inverted.)

The main functions that a "front end" object must then fulfill are:
- to create the implementation object after its own creation (this goes
easily in C++ and other languages that have constructors);
- to exchange the current implementation object into one of another
class when needed;
- to delete the implementation object in conjunction with its own deletion
if there is no garbage collection (thus e.g. in C++,
where this is fortunately easy to implement with destructors).

>Suppose instead that the objects inherited from prototypes, and the mutator
>merely changed the appropriate parent pointers to (e.g.) remove a
>degenerate object's redundant slots and change it's behavior to the less
>costly method. [...]

This reasoning seems to me to be incomplete.
If the programming technique (in a prototype-based language like Self)
is such that the parent objects (Shape, Ellipse, Circle, ...) contain
the methods and the instance objects only the data, then the _behaviour_
can of course be changes by changing the parent pointer.
But that does not magically remove the now superfluous instance variables
from the object! If that is wanted, the object itself must somehow be
modified just as in the class-based case.
In the opposite situation that a circle needs to become an ellipse
it is _necessary_ (and not just a matter of efficiency) that the
additional fields be added to the object.

In this style of programming, Shape, Ellipse, etc. are simply classes
in disguise. A clear disadvantage to real class-based programming
is that one can connect a child to a parent even if the child has not
got all those instance variables that the parent's methods presuppose.

> ...

In article <1990Sep13.235832.23454@Neon.Stanford.EDU> craig@Neon.Stanford.EDU (Craig D. Chambers) replies:
> ...
>To mutate a circle into an ellipse, a new ellipse object would be
>created with the appropriate state, and the parent of the child object
>would be redirected to the ellipse object from the circle object.  All
>clients still point to the child, and so the child appears to have
>"become" an ellipse (or at least its external behavior has changed so
>that its two foci are no longer the same point).  But no weird
>operations (like mutating the formats of objects or redirecting all
>external pointers) are happening, just a single assignment to a single
>(parent) slot.  Dynamic inheritance is *much* more structured and
>clean than become:.

The main reason for having a Circle class at all was to have a compact
and effective representation for an important special case.
According to this suggestion, when a circle becomes a general ellipse,
there will be _both_ a circle object _and_ an ellipse object.
And when an ellipse becomes a circle, there are no storage savings;
at best, the parent objects can be defined so that creating a new
circle object (!) is not necessary.

Markku Sakkinen
Department of Computer Science and Information Systems
University of Jyvaskyla (a's with umlauts)
Seminaarinkatu 15
SF-40100 Jyvaskyla (umlauts again)
Finland
          SAKKINEN@FINJYU.bitnet (alternative network address)

brucec@phoebus.phoebus.labs.tek.com (Bruce Cohen;;50-662;LP=A;) (09/18/90)

I don't have time just now to reply to Markku Sakkinen's posting in detail,
but I did want to make a few comments about areas where I think we're not
talking about quite the same things.  The posting is quite clear on the
problems of a class-based implementation for the problem I described: in
general, inheriting objects may not remove behavior or state variables,
they can only override them.  This is just why I looked for another
solution.  I don't think that we're in as good agreement on the nature of
the prototype-based solution:

In article <1990Sep16.083629.10936@tukki.jyu.fi> sakkinen@tukki.jyu.fi (Markku Sakkinen) writes:

  I wrote:
>>Suppose instead that the objects inherited from prototypes, and the mutator
>>merely changed the appropriate parent pointers to (e.g.) remove a
>>degenerate object's redundant slots and change it's behavior to the less
>>costly method. [...]
> 
> This reasoning seems to me to be incomplete.
> If the programming technique (in a prototype-based language like Self)
> is such that the parent objects (Shape, Ellipse, Circle, ...) contain
> the methods and the instance objects only the data, then the _behaviour_
> can of course be changes by changing the parent pointer.
> But that does not magically remove the now superfluous instance variables
> from the object! If that is wanted, the object itself must somehow be
> modified just as in the class-based case.

No, the data slots are also inherited, and need not be inherited from the
same object the behavior is inherited from.  There is a good example of
just what I wanted to do that explains how data representation can be used
in Self, in the paper "Organizing Programs without Classes" by Ungar,
Chambers, Chang, and Holze, in the Self papers.   In section 2.4, "Beyond
Representation Sharing", they show polygon and rectangle classes which have
different data representations but share the appropriate common behavior.

> 
> The main reason for having a Circle class at all was to have a compact
> and effective representation for an important special case.
> According to this suggestion, when a circle becomes a general ellipse,
> there will be _both_ a circle object _and_ an ellipse object.
> And when an ellipse becomes a circle, there are no storage savings;
> at best, the parent objects can be defined so that creating a new
> circle object (!) is not necessary.

There may be both a circle and an ellipse prototype, but if the instances
in use are modified in place, there only needs to be one instance of each
object, which might behave like (and have the data representation of) a
circle at one time, and an ellipse at another.

--
---------------------------------------------------------------------------
NOTE: USE THIS ADDRESS TO REPLY, REPLY-TO IN HEADER MAY BE BROKEN!
Bruce Cohen, Computer Research Lab        email: brucec@tekcrl.labs.tek.com
Tektronix Laboratories, Tektronix, Inc.                phone: (503)627-5241
M/S 50-662, P.O. Box 500, Beaverton, OR  97077

sakkinen@tukki.jyu.fi (Markku Sakkinen) (09/18/90)

In article <BRUCEC.90Sep17111337@phoebus.phoebus.labs.tek.com> brucec@phoebus.phoebus.labs.tek.com (Bruce Cohen;;50-662;LP=A;) writes:
>I don't have time just now to reply to Markku Sakkinen's posting in detail,
>but I did want to make a few comments about areas where I think we're not
>talking about quite the same things. [...]
>
>In article <1990Sep16.083629.10936@tukki.jyu.fi> sakkinen@tukki.jyu.fi (Markku Sakkinen) writes:
>> [...] then the _behaviour_
>> can of course be changes by changing the parent pointer.
>> But that does not magically remove the now superfluous instance variables
>> from the object! If that is wanted, the object itself must somehow be
>> modified just as in the class-based case.
>
>No, the data slots are also inherited, and need not be inherited from the
>same object the behavior is inherited from.  There is a good example of
>just what I wanted to do that explains how data representation can be used
>in Self, in the paper "Organizing Programs without Classes" by Ungar,
>Chambers, Chang, and Holze, in the Self papers.   In section 2.4, "Beyond
>Representation Sharing", they show polygon and rectangle classes which have
>different data representations but share the appropriate common behavior.

I am sorry, but there seems to be a little confusion still.
In a prototype-based system, "inheriting" a data slot from a parent object
means _sharing_ it with the parent. Surely Bruce Cohen's intention was not
that when a circle becomes a ellipse, it has to share all the instance
variables of some prototype ellipse thenceforth.
(I have not read the paper mentioned, but the above is obvious anyway.)

BTW, you may have to wait a day or two yet for the continuation posting
that I promised in the last one, but it's coming.

Markku Sakkinen
Department of Computer Science and Information Systems
University of Jyvaskyla (a's with umlauts)
Seminaarinkatu 15
SF-40100 Jyvaskyla (umlauts again)
Finland
          SAKKINEN@FINJYU.bitnet (alternative network address)

hoelzle@Neon.Stanford.EDU (Urs Hoelzle) (09/18/90)

sakkinen@tukki.jyu.fi (Markku Sakkinen) writes:

>>[ explanation how circles / ellipses can change representations in a
>>  prototype-based system like Self ]

>I am sorry, but there seems to be a little confusion still.
>In a prototype-based system, "inheriting" a data slot from a parent object
>means _sharing_ it with the parent. Surely Bruce Cohen's intention was not
>that when a circle becomes a ellipse, it has to share all the instance
>variables of some prototype ellipse thenceforth.
>(I have not read the paper mentioned, but the above is obvious anyway.)

I am sorry, but there seems to be a little confusion still... ;-) The
parent object is a *clone* of the {circle,ellipse} prototype, not the
prototype itself.  Every circle has its own "data parent" holding its
state, and all circles have a common parent holding common behavior
(and possibly common state, if needed). With object-based inheritance,
an object can inherit from any other object - there's nothing special
about the prototype.
But I think the above is obvious anyway ;-)

>Markku Sakkinen

jml@wally.altair.fr (Jean Marie Larcheveque) (09/19/90)

I earlier suggested that the circle-to-ellipse mutation could be
handled without using dynamic inheritance, with something like
   class ellipse {
     concrete_ellipse *state_holder;
     // ...
   }
where concrete_ellipse is a superclass of circle. 

However, I made things rather confusing by further suggesting
that concrete_ellipse should be a subclass of ellipse and leaving
out the * in front of "state_holder", and Bruce Cohen rightly
objected:

>But then don't you have to add a set of delegation functions to ellipse so
>an instance of it can pass on calls to it's instance of concrete_ellipse?

Yes indeed! Since we are using composition instead of inheritance
to forward the behavior, we have to make this explicit, so it is
not so neat as in Self, as we program an ad hoc mechanism instead
of using generic mechanisms. However, the fact remains that,
whether we dynamically act on inheritance links or create ad hoc
links to allow switching between 2 types of behavior, this
possibility has to be specified before any object is created. In
one case, creation of a mutable object involves creating both an
identity-holder and a parent behavior-holder, while in the other,
the class definition has to specify behavior delegation. My
knowledge of Smalltalk is insufficient to tell whether become:
leaves the object identifier unmodified; but if such is the case,
it is by far the best solution, as it allows objects to change
classes even when this was not anticipated at the time of their
creation.

--
Jean-Marie Larcheveque  <jml@bdblues.altair.fr> or 
                        <jml@nuri.inria.fr>