[comp.object] Snapshot of Types Discussion

eberard@ajpo.sei.cmu.edu (Edward Berard) (01/07/90)

As promised, this message includes some of the discussion which took
place last August and September on comp.lang.smalltalk. Specifically,
it is the discussion of the possible meanings of "types" in Smalltalk.

I am posting this message for two reasons:

	1. To let people know that there is a variety of views on the
	   meaning of types in an object-oriented programming
	   language, and

	2. To stimulate some discussion on the topic of types and
	   object-oriented thinking.

				-- Ed Berard
				   (301) 353-9652

-----------------------------------------

From: alan@ux.cs.man.ac.uk (Alan Wills)
Newsgroups: comp.lang.smalltalk
Subject: Re: Smalltalk types
Date: 8 Sep 89 10:42:27 GMT
Organization: Dept. of Computer Science, University of Manchester, UK.

Robert Dew's list of sets of Smalltalk classes which understand the
same set of messages is worth contrasting with the notion of a type as
being the set of objects which understand a particular set of
messages.  For example, in Smalltalk, if we look at the set of all
objects which understand the messages "<", "=<", ">", ">=", "=" : we
get all instances of Magnitude (and its subclasses) and all instances
of String (and its subclasses).  Even more interesting is that,
although the implementations of these functions are different in the
two inheritance subtrees, this correspondence isn't just a mere
overloading of names: the relations obey the same rules about how
they're interrelated. For example:

   if I know     and also          then I can infer
   x < y                             (y < x) not
   x =< y       (x = y) not           x < y
   x > y                              y < x
   x < y         y < z                x < z
etc

I guess this helps to illustrate that this notion of type ---
equivalence sets wrt parts of behaviours --- is useful when into the
nitty-gritty of reasoning about programs to check they're going to do
the right things.

Alan Wills
Manchester University, UK
+44-61-273 7121 x5699

--------------------------------------------------------

From: wilson@carcoar.Stanford.EDU (Paul Wilson)
Newsgroups: comp.lang.smalltalk
Subject: types as roles [was Re: Smalltalk types]
Keywords: types, first class types, protocols, type safety, static typing, roles
Date: 12 Sep 89 18:59:09 GMT
Reply-To: wilson@carcoar.Stanford.EDU (Paul Wilson)
Organization: Stanford University

I got several responses to the effect that my types-as-roles model
is very intuitive, but pointing out what at first appears to
be a problem with multiple inheritance.  I don't think it's really
a problem, and in fact is a major strength if you believe in
strong typing.

   [ to recap a little:  I said that types should be *roles* that objects
can play, which is a lot like sets of messages they respond to.  It's
stronger than that, though, because it's an explicit declaration
that the object understands the messages in a particular way.  For
example, what makes an object a number is not just that it understands
messages like ">", but that it promises to respond to them in a way
that makes sense as a number.  This avoids accidental coincidences of
naming, and in software engineering terms helps establish responsibility
for whether something can be used in a particular context.
     I made the example of an object that is a window and is a queue as
well, and can respond to the "size" message as either one;  if you
have a pointer to it as a window, it responds as a window, returning
a point giving its screen dimensions.  If you have a pointer to it
as a queue, it returns an integer telling how many elements are in
the queue. ]

    The problem comes up that you may have a window-queue object with
a message name collision like this, and you may have a pointer to it
as a window-queue, rather than as either a window or a queue.  What
happens then?

    I submit that you should have to explicitly disambiguate the context.
This is not as gross and restrictive as it sounds, and in fact it's not
restrictive at all.  Remember, roles are *external* interfaces, and
may cross-classify with inheritance for code reuse.  If you have a pointer
to something that can behave as a window or behave as a queue, it's
no violation of information hiding to tell it what you mean.

     If you take the conversational message-passing metaphor seriously,
this is exactly how things ought to be.  If I've established more than
one conversational context with somebody, ambiguities must be resolved.
If I'm at a CS conference but I'm talking to somebody I've recently
talked to about windsurfing, a question like "what do you do?" has
to be disambiguated:

    "what do you do? -- as a hacker, I mean --"  vs.
    "what do you do?  Wavesailing, slalom?"

     This is not an invasion of my privacy because both contexts have
already been established.  Presenting myself as a CS guy and a boardhead
does *not* imply, for example, that I'll answer questions about my
religion or political views or whether my family is thoroughly inbred
white trash:  disambiguating *roles* doesn't imply knowing anything about
the *class* of a reciever that it hasn't already told you.

     Oddly, I just came across something like this in a thoroughly
*dynamically* typed language -- Ungar & Smith's "Self" language, which
has no classes at all, just prototypes.  In Self, multiple inheritance
lookups are guided by the "sender path constraint," which sort of like
the kind of disambiguation I'm talking about.  It only works with
messages an object sends to itself, though, because other objects
can't tell anything about the type of the receiver.  Basically,
if an window-queue object sends itself a "size" message while executing
a message inherited from "window," it responds with its window version.
If it's executing a method inherited from "queue" when it sends itself
a "size" message, it likewise assumes it's considering itself a queue
at that point, and responds with the queue size.

     Actually, though, Self isn't making a distinction here between
interface and implementation -- it assumes that they correspond.
In a virtuous strongly-typed language (with all the Truth, Beauty,
and Righteousness that does or doesn't imply), the roles would
be explicit and checked.  Since the size method of a queue is probably
not an implementation of the same interface as the one for a window,
no conflict would occur.  If it *is* the programmer should know
about it and have to say what he/she means.

     In general, I see Ungar's point of view that strong typing is
a hassle and a pain in the ass.  But I'm not sure you couldn't get
rid of most of the hassle by the use of defaults and a little
automation.  For example, when you implement a subclass, it would
be, by default, a subtype of the type(s) of its parent(s).  And if
you write code that assigns a particular value to an untyped
variable, the type is inferred, etc.  In general, pretty safe
guesses would be made and the programmer's attention brought to
the ambiguities.  The latter might be deferred until runtime
errors, or a tightening-up phase after a prototyping phase.  You
might have an optionally-typed language with automated support for going
back in and strongly-typing things as needed.  (Either to satisfy
your anal-retentive supervisor :-) or if you feel that maybe your
program's doing the wrong thing because you have undetected role
mismatches.)

     It seems to me that programmers generally have a very good idea
of what roles are appropriate at what point in a piece of code, and
the trouble is not in any serious brainwork or overcommitment that
the programmer must do, but in some low-level explication that might
be largely automated, getting the best of both worlds.  (Note that
the programmer could always use the degenerate role "object" for
things that must truly be dynamically typed.  But for things in
which the intent of the code obviously restricts the plausible roles
anyway, like generic numeric code, specifying the role "number" is not
restrictive.  And if in some cases the programmer overcommits, then
there should be automated support for generalizing, like going back
and substituting a more general role and pointing out any mismatches
or ambiguities it causes.  This might actully be *more* flexible
than dynamic typing, because some of the "freedom" of dynamic
typing creates more work by making it harder to find hidden
assumptions and dependencies.  If you overcommited when you wrote
the code, you might as well have done it explicitly, so a tool
can help you fix it.)

BTW,  David Keppel has pointed out to me that Eiffel has a renaming
facility, but it's really not doing the kind of disambiguation that
I mean.  It's for disambiguating name collisions *across* contexts,
to establish a single non-role-dependent interface.  This seems wrong
and against the object-oriented grain to me.  If an object is a
queue and a window, it should be able to function both as a queue and
as a window, without breaking existing code.  The window management
software should not have to be changed, and neither should software
that uses queues.  It should not matter to either one what some
object does in other contexts, just as it is irrelevant that a
word in English means something else in French.  The introduction of
bilingual entities should not require changes to either language,
unless there's some fortuitous advantage to it.


Paul R. Wilson
Software Systems Laboratory               lab ph.: (312) 996-9216
U. of Illin. at C. EECS Dept. (M/C 154)   wilson@carcoar.stanford.edu
Box 4348   Chicago,IL 60680

----------------------------------------------------------
From: johnson@p.cs.uiuc.edu
Newsgroups: comp.lang.smalltalk
Subject: Re: types with identity > protocols, no
Date: 12 Sep 89 03:41:00 GMT

>What happens when an object is both a queue and a window (say,
>because it's a queue that can represent its contents to the
>user)?

>It seems to me that the "right," intuitive way to do this,
>in a strongly typed system, is to resolve this by the
>role the object is playing.  If the window system has
>a pointer to it *as a window*, it should respond as
>a window.  And if a simulation object has a pointer to
>it *as a queue*, it should respond as a queue.

>Does anybody do types this way?

I don't do types that way, but I do OBJECTS that way.  In other words,
I wouldn't define one object, but two separate objects that make up
a multifaceted object.  If you want a window then you get a window
object (probably the "window" message retrieves it) and if you want
a queue then you get a queue object.  The queue and the window know
about each other and can be thought of as different facets of a complex
object, but are implemented as separate objects.  This seems simple
and easy both to implement and to think about.  Most o-o windowing
systems work this way.

Ralph Johnson

-------------------------------------------------------------
From: johnson@p.cs.uiuc.edu
Newsgroups: comp.lang.smalltalk
Subject: Typed Smalltalk at OOPSLA
Date: 14 Sep 89 03:56:00 GMT

The goal of the Typed Smalltalk project is to build an optimizing
compiler for Smalltalk.  We are not trying to invent a new programming
language, and we consider the Smalltalk programming environment just
as important as the language, so we expect the browser and debugger
to work pretty much like in Smalltalk-80.  The major difference
that the user sees from Smalltalk-80 is that methods have types; the
compiler needs this information for optimization.  Fortunately, we
have had great success with type inference, so the type system should
not destroy the advantages of Smalltalk for rapid-prototyping.
The compiler produces "good" code; it is very fast when it works.
As is typical of university projects, the compiler is still unreliable,
but there are good reasons to hope that it will be reliable soon.

The compiler currently runs inside Smalltalk-80 and produces machine
language that can be executed from user primitives.  The result is
esentially the ability to write user primitives in Smalltalk.  Our
long term goal is to compile the entire image and ignore the interpreter,
to make it easy to integrate Smalltalk with other languages, and to
make it easy to run Smalltalk programs outside the image, but we still
have a bit of work to do before we achieve these goals.

I will be at OOPSLA and am interested in talking with people about
Typed Smalltalk.  Monday is completely open.  Since many people
are coming later, a "birds of a feather" session during an evening
might be a better idea.  In any case, you can come and look for an
announcement or you can try to get in touch with me before-hand.

Ralph Johnson  -- University of Illinois at Urbana-Champaign
johnson@cs.uiuc.edu       (217) 244-0093

-----------------------------------------------------
From: scc@cl.cam.ac.uk (Stephen Crawley)
Newsgroups: comp.lang.smalltalk
Subject: Re: types as roles
Keywords: types, first class types, protocols, type safety, static typing, roles
Date: 16 Sep 89 01:04:38 GMT
Organization: U of Cambridge Comp Lab, UK

Paul Wilson's types-as-roles model bears more than a passing resemblance
to the type system of the distributed persistent object system that
I am working on.

Objects or *entities* are characterised 3 components; a *class* which defines
an abstract interface, an *implementation* that provides the routines etc
that implement the operations defined by the class, and the *representation*
which is understood by the implementation routines.  (The separation of
interface from code in my type system allows a class to have a number of
implementations.)

Entities exist in two states.  *Passive* entities have all their state in
stable store and are signified by an *identifier* or *id*.  *Active*
entities may have some of their state in local (non-stable) memory and
are signified by a *handle* whose type is a class.  A handle for an
entity is obtained by applying a classes *open* operation, passing the
entity id as an argument.

When a client wants to use an entity (i.e. to invoke operations), it must
have a handle for the entity.  The class of the handle determines the
operations the client may invoke.  This class is called the client's
*perspective class*.  On the other hand the entity's *actual class* is
the class that corresponds to the entity's implementation.

Normally, the client's perspective class and the entity's actual class
are the same.  If they are not, the client will invoke operations defined
by the perspective class, and these operations will be *projected* onto
operations provided by the actual class.  The *projection* is set up as
the entity is opened by a piece of code called a *resolution function*.

Projections may be simple (i.e. a renaming or a restriction) or
arbitrarily complex depending on the resolution function.  It was
originally intended that the mechanism be used for restricting
access to an entity's operations, and for making evolutionary
changes in a persistent type system.  In practice, projection turns
out to be far more powerful mechanism.  We have used it to perform
a variety of tasks ranging from displaying objects and synthesising
interactive tools through to selecting versions in a version control
system.

The current version of the entity type system does not support
static inheritance (i.e. classes don't have subclasses).  It will
do, but I haven't got around to it.  I'm currently concentrating
more on garbage collection and distributed programming aspects.

For further information, check out the following paper:

"Flexibility in a Persistent Object-based Type System"
   H.J. Barman and S.C. Crawley in
   Proceedings of the Workshop on Persistent Object Systems,
     Appin Scotland, August 1987
   Persistent Programming Research Report #44
   University of Glasgow, Dept of C.S.

The address for U of G, CS is
   University of Glasgow, Dept of Computing Science,
   Lilybank Gardens,
   Glasgow G12 8QQ
   Great Britain.

Papers on the latest incarnation of the Entity System are in the pipeline.

-- Steve