Instructor's Guide
intro,
paradigms,
comparison,
design,
prototypes,
architectures,
summary,
Q/A,
literature
A class provides a generic description
of one or more objects, its instances.
From a formal point of view, classes are related to types,
and hence a class may be said to correspond to
the set of instances that may be generated from it.
This viewpoint leads to some anomalies,
as in the case of abstract
classes that at best correspond to partially defined sets.
As another problem, in the context of inheritance,
behavioral compatibility may be hard to arrive at,
and hence the notion of subtype
(which roughly corresponds with the subset relation)
may be too restrictive.
In practice, we may further encounter
one-of-a-kind objects, for which it is simply cumbersome
to construct an independent class.
In a by now classical paper, [Lieber]
proposes the use of prototypes
instead of classes.
The notion of prototypes (or exemplars) has
been used
in cognitive psychology
to explain the incremental nature of concept learning.
As [Lieber] notes, the philosophical
distinction between prototypes
(which provide a representative example of an object)
and classes
(which characterize a set of similar objects)
may have important pragmatical consequences
as it concerns the incremental definition
of (hierarchies of) related objects.
First, it is (claims Lieberman, 1986)
more natural to start from a concrete example than to
start from an abstract characterization as given by a class.
And secondly, sharing information between prototypes
and clones (that is, modified copies) thereof
is far more flexible than the rather static means of sharing code
as supported by the class inheritance mechanism.
Code sharing by inheritance may be characterized as
creation time sharing,
which in this respect is similar to creating a copy
of the object by cloning.
In addition, prototypes may also support
lifetime resource sharing by means of delegation.
In principle, delegation is nothing but the forwarding
of a message.
However, in contrast to the forwarding mechanism
as described in
sections [delegation-in-Java]
and [hush-idioms],
delegation in
the context of prototypes does not change implicit
self-reference to the forwarding object.
In other words, when delegating a message to a
parent object, the context of answering the message
remains the same, as if the forwarding object
answers the request directly.
See slide [5-prototypes].
Prototypes -- exemplars
- cloning -- creation time sharing
- delegation -- lifetime sharing
slide: Prototypes
An almost classical example used to illustrate
prototypical programming is the example
of a turtle object that delegates its request to
move itself to a pen object
(which has x and y coordinate attributes and a move method).
The flexibility of delegation becomes apparent
when we define a number of turtle objects by cloning
the pen object and adding an y coordinate
private to each turtle.
In contrast to derivation by inheritance,
the x coordinate of the pen object
is shared dynamically.
When changing the value of x in one of the turtle
objects, all the turtle objects will be affected.
Evidently, this allows considerable
(and sometimes unwished for) flexibility.
However, for applications (such as multimedia systems)
such flexibility may be desirable.
Design issues
Strictly speaking, prototype-based delegation is not
stronger than forwarding in languages
supporting classes and inheritance.
In [Dony], a taxonomy of prototype-based
languages is given.
(This taxonomy has been partly implemented in Smalltalk.
The implementation, however, employs so-called
class-variables, which are not unproblematic
themselves. See section [meta].)
One of the principal advantages of prototype-based
languages is that they offer a consistent yet simple model
of programming, consisting of objects, cloning and
delegation.
Yet, when designing a prototype-based language,
a number of design decisions must be made
(as reflected in the taxonomy given in Dony {\it et al.}, 1992).
These issues concern the representation of the state of an object,
how objects are created and the way in which delegation is handled.
See slide [5-proto].
State
- slots -- parents
- variables and methods
Creation
- shallow cloning
- deep cloning
Delegation
- implicit delegation
- explicit delegation
slide: Prototypes -- state, creation, delegation
The basic prototype model only features slots
which may store either a value or a piece of
code that may be executed as a method.
Alternatively, a distinction may be made between
variables and methods.
In both cases, late binding must be employed
to access a value.
In contrast, instance variable bindings in class-based
languages are usually resolved statically.
When creating a new object by cloning an existing object,
we have the choice between deep copying and shallow copying.
Only shallow copying, however, allows lifetime sharing
(since deep copying results in a replica at creation time).
Shallow copying is thus the obvious choice.
Finally, delegation is usually handled implicitly,
for instance by means of a special parent slot,
indicating the ancestor of the object (which
may be changed dynamically).
Alternatively, it may be required to indicate
delegation explicitly for each method.
This gives a programmer more flexibility
since it allows an object to have multiple ancestors,
but at the price of an increase in notational complexity.
Explicit delegation, by the way,
most closely resembles the use of forwarding
in class-based systems.
One of the, as yet, unresolved problems
of delegation-based computing is how to deal with
what [Dony] call split objects.
An object may (internally) consist
of a large number of (smaller) objects that
are linked to each other by the delegation relation.
It is not clear how to address such a complex
object as a single entity.
Also, the existence of a large number of small objects
that communicate by message passing may impose
severe performance penalties.