[comp.object] CHALLENGE: heterogeneous collections

pallas@eng.sun.com (Joseph Pallas) (03/26/91)

In <1991Mar22.210725.29448@neon.Stanford.EDU>
hoelzle@neon.Stanford.EDU (Urs Hoelzle) writes:

>But you aren't convinced and I've heard this argument often enough,
>so let me give an example.  Please show me how you would use the many
>features of Ada to write this in Ada (or any other statically-typed
>language):

>[example of heterogeneous list of "unrelated" objects deleted]

>To avoid misunderstandings, please note that the above example *is*
>type-safe in an abstract sense: no 'message not understood' error can
>occur because all elements of listC understand both 'foo' (which may
>be sent by someProc) and 'display'.  However, I claim that this piece
>of code won't type-check in Ada, C++, Eiffel, ...

Sooner or later, opponents of static typing bring out the
heterogeneous collection.  There's no question that it isn't possible
with most traditional language type-systems to express the type of the
list in Urs's example.  Type systems that can express union types
(such as, "an A or a B") or purely extensional types (such as,
"anything that understands 'foo'") [is that the right name, type
theory mavens?]  are not common (but not unknown, either---I think
Ralph Johnson's Typed Smalltalk can do this).

The problem is, no one has ever come up with a convincing reason why I
should want my type system to handle the heterogeneous collection.
This is because no one has come up with a convincing reason why I
should want to write programs that contain heterogeneous collections.
Needing collections of otherwise-unrelated objects generally signals a
flaw in the design, not a failing of the type system.

joe

hoelzle@leland.Stanford.EDU (urs hoelzle) (03/26/91)

In article <pallas.669923769@red-dwarf>, pallas@eng.sun.com (Joseph Pallas) writes:
|> 
|> Sooner or later, opponents of static typing bring out the
|> heterogeneous collection.  
|> [stuff deleted]
|> The problem is, no one has ever come up with a convincing reason why I
|> should want my type system to handle the heterogeneous collection.
|> This is because no one has come up with a convincing reason why I
|> should want to write programs that contain heterogeneous collections.
|> Needing collections of otherwise-unrelated objects generally signals a
|> flaw in the design, not a failing of the type system.

I think a few clarifications are in order because you're attacking on
the wrong front, Joe:

1.  I am not an "opponent of static typing".  For some kinds of problems, they
    have advantages, and for others they have disadvantages.  But I do disagree
    with people who say that typing does not impair reusability, or who say
    "I can do that in <your favorite typed language here>, too".

2.  Here's why you want at least some form of heterogeneous collections:
	
	type T = "some type"
	type SubT = "subtype of T"

	procedure someProc(c: List[T])

    I think that you'll agree with me that you'd like to use someProc with
    a Collection[SubT], especially if someProc just iterates through the
    list and sends 'foo' -- after all, every SubT "is a" T.  But as soon as
    Lists are mutable (e.g. define insert(newElem: T)), the type system
    won't let you do it because List[SubT] is *not* a subtype of List[T].

    You could try to fix this by splitting List into ReadOnlyList and a
    subtype ReadWriteList (which adds the insert operation), but this
    begins to stretch the correspondence between types and concepts (IMHO).
    And it certainly doesn't scale up very well, e.g. when you try to
    subclass List or when you have several methods like "insert" which
    you need to "turn off" in order to use someProc.

    In a way, the problem exists because today's type systems are based
    on what you *could* do to the list in someProc, not on what you actually
    do.  

3.  At least for rapid prototyping, your argument that "the type hierarchy
    isn't perfect if this situation occurs" is invalid: you *don't* want to
    rearrange lots of types just to try something out.  Instead, you want to
    combine existing functionality in new ways which you didn't anticipate
    when you originally wrote the code.  When (and if) you convert your prototype
    into a "product", *then* you go back and clean up the type hierarchies.

    Even in "production" programming, life isn't perfect and you might not
    be able to rearrange the type hierarchy because a) you're using some
    library or b) different uses have conflicting views, i.e. require
    conflicting type hierarchies.

-Urs

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (03/26/91)

In article <1991Mar25.220525.11087@leland.Stanford.EDU> hoelzle@leland.Stanford.EDU (urs hoelzle) writes:
> 2.  Here's why you want at least some form of heterogeneous collections:
> 	type T = "some type"
> 	type SubT = "subtype of T"
> 	procedure someProc(c: List[T])
>     I think that you'll agree with me that you'd like to use someProc with
>     a Collection[SubT], especially if someProc just iterates through the
>     list and sends 'foo' -- after all, every SubT "is a" T.  But as soon as
>     Lists are mutable (e.g. define insert(newElem: T)), the type system
>     won't let you do it because List[SubT] is *not* a subtype of List[T].

``Subtype? What's a subtype?'' says the C programmer.

To implement a generic list type in C, you use macros. Nowhere is there
an explicit ``insert'' operation that demands its own prototypes. So if
you do have an operation that works on multiple types, it will work
correctly through a list.

Maybe one ``object-oriented'' language or another works badly with
lists, but that's not related to static versus dynamic typing.

So: What use can anyone make of a heterogeneous list?

---Dan

boissier@irisa.fr (franck boissiere) (03/26/91)

> In article <pallas.669923769@red-dwarf>, pallas@eng.sun.com (Joseph Pallas) writes:
> |> 
> |> Sooner or later, opponents of static typing bring out the
> |> heterogeneous collection.  
> |> [stuff deleted]
> |> The problem is, no one has ever come up with a convincing reason why I
> |> should want my type system to handle the heterogeneous collection.
> |> This is because no one has come up with a convincing reason why I
> |> should want to write programs that contain heterogeneous collections.
> |> Needing collections of otherwise-unrelated objects generally signals a
> |> flaw in the design, not a failing of the type system.
> 

There is at least one kind of system which must allow heterogeneous
collections. It is often called Open System. Is these systems the
person who writes the kernel do not know anything about what objects
will be used by the persons who will extend the system.

Another exemple is for debugging, where you might collect any kind
of objects.


-- 
Franck BOISSIERE                        boissier@irisa.irisa.fr
C.C.E.T.T.   B.P. 59                    boissiere@ccett.fr
35512 CESSON SEVIGNE CEDEX  FRANCE    

mto@harvey.gte.com (Tamer Ozsu) (03/26/91)

In article <pallas.669923769@red-dwarf> pallas@eng.sun.com (Joseph Pallas) writes:
>The problem is, no one has ever come up with a convincing reason why I
>should want my type system to handle the heterogeneous collection.
>This is because no one has come up with a convincing reason why I
>should want to write programs that contain heterogeneous collections.
>Needing collections of otherwise-unrelated objects generally signals a
>flaw in the design, not a failing of the type system.
>

Well, let me see how I can take a position on both sides of this
argument. I must admit that I missed the earlier segments, so if I am
reiterating something that has been already addressed, my apologies.

There are cases in object-oriented database systems where the results
of a query is a heterogeneous collection. Object algebras take as
input sets of objects and produce as output sets of objects (sometimes
new objects that were not in the database to begin with -- e.g.,
explicit join -- but that is not important here). It is quite possible
that the result set of objects is a heterogeneous collection. So,
there is a need for the ability to handle heterogeneous sets of
objects. 

On the other hand, I don't see why a type system and type checking
mechanism cannot be developed to handle these collections.  Some
object algebras impose what is called union compatibility on the
algebra operators [Shaw and Zdonik paper in the 6th Data Engineering
Conference, 1990]. Union compatibility requires that members of the
sets being operated on to be instances of types which are in a subtype
relationship with one another. However, this may be too restricted. In
last year's OOPSLA (1990), we presented a paper [pages 224-233] that
discusses a more general type system for a simplified object algebra.
The paper also discusses the issues of heterogeneous sets. This is by
no means the definitive word on the subject, but it demonstrates that
a type system, which is sometimes more restricted than one may like,
can be developed.

==Tamer



--
M. Tamer Ozsu				Telephone:	(617) 466-2098	
GTE Laboratories			      Fax:	(617) 290-0628
40 Sylvan Road				 Internet:	mto@gte.com
Waltham, MA 02254

pallas@eng.sun.com (Joseph Pallas) (03/27/91)

In <1991Mar25.220525.11087@leland.Stanford.EDU>
hoelzle@leland.Stanford.EDU (urs hoelzle) writes:

>I think a few clarifications are in order because you're attacking on
>the wrong front, Joe:

I didn't intend to attack, just to make a point.  Heterogeneous
collections just happens to be one of my buttons.  Also, I shouldn't
have portrayed you as an "opponent of static typing" when you were
merely presenting one argument against static typing.

>2.  Here's why you want at least some form of heterogeneous collections:
>	
>	type T = "some type"
>	type SubT = "subtype of T"

>	procedure someProc(c: List[T])

>    I think that you'll agree with me that you'd like to use someProc with
>    a Collection[SubT], especially if someProc just iterates through the
>    list and sends 'foo' -- after all, every SubT "is a" T.  But as soon as
>    Lists are mutable (e.g. define insert(newElem: T)), the type system
>    won't let you do it because List[SubT] is *not* a subtype of List[T].

Aha!  There's no heterogeneous collection here.  There is, however, a
problem with the notion of subtyping for parameter conformance.  One
solution to an overly restrictive static type checker is to weaken the
static checking.  Another solution, however, would be a smarter
checker.

>    In a way, the problem exists because today's type systems are based
>    on what you *could* do to the list in someProc, not on what you actually
>    do.  

Indeed.  If I could provide more information about someProc, such as
the fact that it never attempts to modify its parameter (did I hear
someone cough "const"?), then even a simple type checker would be
happy to approve this operation.  Urs is correct that subtyping is NOT
the right way to express this relationship.  Subtyping is too strict a
conformance relation.

>3.  At least for rapid prototyping, your argument that "the type
>    hierarchy isn't perfect if this situation occurs" is invalid: you
>    *don't* want to rearrange lots of types just to try something
>    out.

This is a fairly strong argument.  A good programming environment
could go a long way toward neutralizing it, however.  I confess I'm
not aware of any that really eliminates all the effort involved in
making this kind of change.  On the other hand, I rely on static type
checking during my rapid prototyping.  It helps me to keep my mental
model synchronized with the code I'm actually manipulating.  I also
find that the more OOP I do, the less often that I make major changes
during prototyping without going all the way back to rethink the
design.  Maybe I'm fossilizing.

There are just opinions, of course, and your mileage may vary.

joe

boehm@parc.xerox.com (Hans Boehm) (03/27/91)

hoelzle@leland.Stanford.EDU (urs hoelzle) writes:

>2.  Here's why you want at least some form of heterogeneous collections:
>	
>	type T = "some type"
>	type SubT = "subtype of T"

>	procedure someProc(c: List[T])

>    I think that you'll agree with me that you'd like to use someProc with
>    a Collection[SubT], especially if someProc just iterates through the
>    list and sends 'foo' -- after all, every SubT "is a" T.  But as soon as
>    Lists are mutable (e.g. define insert(newElem: T)), the type system
>    won't let you do it because List[SubT] is *not* a subtype of List[T].

As someone who hasn't yet completely converted to the object oriented religion,
this has always struck me as using the wrong tool for the job.  I really don't
want a "list of T or subtype of T".  I want a "list of objects that understand
foo".  Once I take that view, I have no problems, at least in the few
statically typed languages that allow this (e.g. Russell, and probably FX from
MIT, and probably Scratchpad II from IBM).  I also don't need to construct
my types in such a way that all types that understand foo are "derived" from
a common supertype;  I can use inheritance purely where it simplifies the
implementation.  And I get full static checking, though elements of the list
will, of course, somehow carry the "foo" function with them.  (In Russell, I
would build a list of pairs <x: T, T: [algebra containing foo]>.)

Hans-J. Boehm
(boehm@xerox.com)

objtch@extro.ucc.su.oz.au (Peter Goodall) (03/27/91)

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:

> ...
>So: What use can anyone make of a heterogeneous list?

>---Dan

I have written a Smalltalk application which will unload fairly arbitary
recursive objects to an append only Stream (pipe). We use this for
spitting windows and their contents between workstations on a network.
There is no way of knowing the contents of a window structure 
because they are very dynamic.

When I write the structure out to a file I keep a heterogeneous Dictionary
which has as keys each object encountered as the structure is traversed.
The valu in the dictionary at an object is a unique number from a counter.

So, if I find a new object, I increment the counter and store its value
in the dictionary at the new object. Each time I encounter an object in
the traversal I check the dictionary if it is already there I put its
unique integer in the output stream.

When I load a structure from a stream into the curent image I put each object
as I re-create it into a heterogeneous list. It's index in this list
is the unique number assigned in the output dictionary. When I encounter
a reference number on input replace it with inputListAt: anInteger.

While the structure of this text may not be too good. I believe this is 
an application where heterogeneous maniplulation of objects is essential.


Peter Goodall

peter@ficc.ferranti.com (Peter da Silva) (03/27/91)

In article <26714:Mar2602:52:1891@kramden.acf.nyu.edu> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:
> So: What use can anyone make of a heterogeneous list?

Message queue for a real-time operating system?
-- 
Peter da Silva.  `-_-'  peter@ferranti.com
+1 713 274 5180.  'U`  "Have you hugged your wolf today?"

objtch@extro.ucc.su.oz.au (Peter Goodall) (03/27/91)

kramden.acf.nyu.edu (Dan Bernstein) writes:

> ...
>So: What use can anyone make of a heterogeneous list?

>---Dan

Allow me to turn my previous posting into something more  coherent.   (I
hate writing in UNIX editors) :-).

I  spend  quite  a lot of time working on system functions in a client's
Options Trading System.  They have a need to  pass  Smalltalk/V  windows
between  workstations  on  a  network.   These  windows  are essentially
applications.  They are all different in  structure  and  functionality.
For  example a portfolio browser.  This browser is not just some buttons
and bitmaps, it contains the full  details  of  the  portfolio  and  all
associated objects.

I  wrote,  some  code  to  write  arbitrary, perhaps recursivley defined
objects, serially to a file or pipe.  Because this tool has to work with
classes  and  structures  which  evolve  over time there is no way I can
predict which classes the tool will have to deal with at any  particular
time.

To  manage  repeated  and recursive references to an object on output, I
recursively traverse the structure of the object to  unload,  and  enter
each  object  into  a  dictionary  as it is encountered.  The key to the
dictionary is  the  object  itself.   A  HETEROGENEOUS  DICTIONARY.  The
dictionary  entry  at each object is an integer identifier which denotes
that object as the n'th object encountered on traversal.

If I've seen the object before on traversal then  I  write  its  integer
identifier to the output.  This handles recursion and repetition.

When  loading  the  complex  object  from  the  Stream into Smalltalk on
another  workstation  Each  object  is  stored  on   the   end   of   an
OrderedCollection (A HETEROGENEOUS LIST) , when an integer identifier is
encountered in the input Stream, I replace it with  the  object  in  the
OrderedCollection indexed by that integer.

The  Dictionary and the OrderedCollection must be Heterogeneous for such
a tool to work.


Peter Goodall (Sorry about the repost)

hoelzle@elaine13.Stanford.EDU (urs hoelzle) (03/27/91)

> To implement a generic list type in C, you use macros. Nowhere is there
> an explicit ``insert'' operation that demands its own prototypes. So if
> you do have an operation that works on multiple types, it will work
> correctly through a list.
>
> Maybe one ``object-oriented'' language or another works badly with
> lists, but that's not related to static versus dynamic typing.

Really.   I'm not sure I understand your solution, so could you please 
post it?  Especially (if it is what I think it is), how does it generalize
to larger problems?  Just write more macros, I guess... 

Seriously: all programming languages are Turing-equivalent, so I'm sure
you can do everything in C.  But what about module interfaces (typed, of
course), abstraction etc?  How do you preserve them with macros?  How do 
handle non-toy examples where the equivalent to "insert" is a complicated
piece of code?  Do you really want an "inlined" copy of that code at every 
call site?

-Urs



--
------------------------------------------------------------------------------
Urs Hoelzle, CS PhD student			       hoelzle@cs.stanford.EDU
Center for Integrated Systems, CIS 42, Stanford University, Stanford, CA 94305

yodaiken@chelm.cs.umass.edu (victor yodaiken) (03/27/91)

In article <6.9AL38@xds13.ferranti.com> peter@ficc.ferranti.com (Peter da Silva) writes:
>In article <26714:Mar2602:52:1891@kramden.acf.nyu.edu> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:
>> So: What use can anyone make of a heterogeneous list?
>
>Message queue for a real-time operating system?
>-- 

We're going to write a real-time operating system in a language with
major run-time components?


>Peter da Silva.  `-_-'  peter@ferranti.com
>+1 713 274 5180.  'U`  "Have you hugged your wolf today?"

hoelzle@leland.Stanford.EDU (urs hoelzle) (03/28/91)

> As someone who hasn't yet completely converted to the object oriented religion,
> this has always struck me as using the wrong tool for the job.  I really don't
> want a "list of T or subtype of T".  I want a "list of objects that understand
> foo".  Once I take that view, I have no problems, at least in the few
> statically typed languages that allow this (e.g. Russell, and probably FX from
> MIT, and probably Scratchpad II from IBM).  I also don't need to construct
> my types in such a way that all types that understand foo are "derived" from
> a common supertype;  I can use inheritance purely where it simplifies the
> implementation.  And I get full static checking, though elements of the list
> will, of course, somehow carry the "foo" function with them.  (In Russell, I
> would build a list of pairs <x: T, T: [algebra containing foo]>.)


This sounds good.  However, I suspect that these languages don't (didn't?)
have inheritance at all...?  Plus, some people don't like functional languages.
Could you post some pointers to the relevant literature?

Thanks,

-Urs

------------------------------------------------------------------------------
Urs Hoelzle                 			       hoelzle@cs.stanford.EDU
Center for Integrated Systems, CIS 42, Stanford University, Stanford, CA 94305

bertrand@eiffel.UUCP (Bertrand Meyer) (03/28/91)

From <pallas.669923769@red-dwarf> by pallas@eng.sun.com (Joseph Pallas)
quoted by boissier@irisa.fr (franck boissiere) in
<1991Mar26.101051.29527@irisa.fr>:

> > |> Sooner or later, opponents of static typing bring out the
> > |> heterogeneous collection.  

To handle heterogeneous collections without endangering static
typing, use genericity (Eiffel syntax):

	class COLLECTION [G] ...

G, the formal generic parameter, describes the arbitrary
type of collection elements. Clients obtain actual collections
by providing some arbitrary type as actual generic parameter for G:

	my_fossil_collection: COLLECTION [FOSSIL]

This is known as unconstrained genericity and works
if no special property is required of collection elements;
in other words, the only operations that class COLLECTION
may apply to entities of type G are those operations which
are defined for all types.

If the elements of the collection must have specific
properties, describe these properties by a class
SPECIFIC and use constrained genericity as follows:

	class SPECIFIC_COLLECTION [G -> SPECIFIC] ...

This means that any actual generic parameter
must be a descendant of SPECIFIC. In other words,
the declaration

	your_stamp_collection: SPECIFIC_COLLECTION [STAMP]

will be valid if and only if STAMP inherits, directly
or indirectly, from SPECIFIC.

As a direct consequence of declaring G as constrained
by SPECIFIC, class SPECIFIC_COLLECTION may manipulate
entities of type G and apply to them any operation
defined in class SPECIFIC. For example, it could
include a routine

	some_operation (s: G) is
		do
			s.spec
		end

where `spec' is a routine declared in class SPECIFIC.

The only practical restriction on clients of such a collection
class, then, is that if they want instances of a certain class
(such as STAMP in the example) to appear in ``specific'' collections,
they must add SPECIFIC to the list of parents of that class.

This is a reasonable requirement: the ability to appear
in such a collection is part of the abstract properties of these
instances.

Of course, the scheme only works if multiple
inheritance is permitted, so that we can add SPECIFIC to the 
list of parents of a class which may already have other
parents, describing other kinds of inherited properties.

-- Bertrand Meyer
bertrand@eiffel.uucp

P.S. After writing this I saw an answer to a message
by Erland Sommarskog; that answer appears to indicate that
the above points may have been made already. I didn't
see Mr. Sommarskog's message itself, however. My apologies
if the above just repeats comments already presented by others.

pallas@eng.sun.com (Joseph Pallas) (03/28/91)

In <objtch.670045723@extro> objtch@extro.ucc.su.oz.au (Peter Goodall)
writes about a collection of Objects in Smalltalk.  But a collection
of Objects is not a heterogeneous collection.  This may be a little
confusing because Object is a universal supertype in Smalltalk.

Let's be concrete:

A heterogeneous collection is one for which no single type can be
safely assigned to all members of the collection.

If all of the members of the collection have some common supertype,
AND no operation is performed on any member of the collection that is
not valid for that common supertype, then the collection is a
homogeneous collection of members of that supertype.

So if you have a collection of objects, you have to do something to
those objects that you can't safely do to Objects in order to have a
heterogeneous collection.

joe

hoelzle@leland.Stanford.EDU (urs hoelzle) (03/28/91)

In article <pallas.670008199@red-dwarf>, pallas@eng.sun.com (Joseph Pallas) writes:
|> 
|> >    In a way, the problem exists because today's type systems are based
|> >    on what you *could* do to the list in someProc, not on what you actually
|> >    do.  
|> 
|> Indeed.  If I could provide more information about someProc, such as
|> the fact that it never attempts to modify its parameter (did I hear
|> someone cough "const"?), then even a simple type checker would be
|> happy to approve this operation.  Urs is correct that subtyping is NOT
|> the right way to express this relationship.  Subtyping is too strict a
|> conformance relation.

The problem is (unfortunately) harder than "const": if you have a generic
type (say List), e.g.:

type List[T] = 
	......
	procedure f(arg: T);

then the above problem pops up, even if f doesn't change the List (e.g.
"const arg: T" wouldn't change the problem a bit).  The problem is 
contravariance: for a List[SubT], f's signature is "procedure taking a SubT",
i.e. a more restricted type than T, violating contravariance.

Before any Eiffel fans say "Gotcha!" :-), let me go on to say that 
contravariance isn't really the *problem*, just a (correct) formulation of it:
if List[SubT] were a subtype of List[T], some variable v: List[T] could
contain either a List[T] or a List[SubT].  However, the statement
"v.f(t)" (with t:T) would be unsafe: if t holds a T, and v a List[SubT],
we're passing only a T to f, not a SubT as expected (declared).

So, again, the problem isn't contravariance, but how you apply it.

-Urs

------------------------------------------------------------------------------
Urs Hoelzle                 			       hoelzle@cs.stanford.EDU
Center for Integrated Systems, CIS 42, Stanford University, Stanford, CA 94305

chip@tct.uucp (Chip Salzenberg) (03/28/91)

According to hoelzle@leland.Stanford.EDU (urs hoelzle):
>2.  Here's why you want at least some form of heterogeneous collections:
>	
>	type T = "some type"
>	type SubT = "subtype of T"
>
>	procedure someProc(c: List[T])
>
>    I think that you'll agree with me that you'd like to use someProc with
>    a Collection[SubT] ...

Actually, I don't agree.  And the burden of proof lay with the person
who says "X is necessary."

>    In a way, the problem exists because today's type systems are based on
>    what you *could* do to the list in someProc, not on what you actually do.

Without giving the compiler knowledge of the implementation of
someProc, that restriction is rather difficult to escape.

>3.  At least for rapid prototyping, your argument that "the type hierarchy
>    isn't perfect if this situation occurs" is invalid ...

Granted.  Always tradeoffs...  We here don't do rapid prototyping.
-- 
Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>
   "All this is conjecture of course, since I *only* post in the nude.
    Nothing comes between me and my t.b.  Nothing."   -- Bill Coderre

chip@tct.uucp (Chip Salzenberg) (03/28/91)

According to objtch@extro.ucc.su.oz.au (Peter Goodall):
>I have written a Smalltalk application which will unload fairly arbitary
>recursive objects to an append only Stream (pipe).

But not _totally_ arbitrary objects, because the objects so dumped
must understand a dumpTo: method, right?

C++ can easily solve this problem using multiple inheritance, virtual
functions, and vanilla homogenous lists.

Next contestant?
-- 
Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>
   "All this is conjecture of course, since I *only* post in the nude.
    Nothing comes between me and my t.b.  Nothing."   -- Bill Coderre

cs450a03@uc780.umd.edu (03/28/91)

Urs Hoelzle writes:
>Suppose you have two classes T and SubT, where SubT is a subtype of T
>(in the strict sense of `subtype').  You also have the lists
> ...
>Now the problem: List[T] is not a subtype of List[SubT] because of the
>insert method (contravariance).  Thus, someProc(listB) is illegal even
> ...

That's not a problem, that's a slice of code.

Raul Rockwell

hoelzle@leland.Stanford.EDU (urs hoelzle) (03/28/91)

In article <519@eiffel.UUCP>, bertrand@eiffel.UUCP (Bertrand Meyer) writes:
|> From <pallas.669923769@red-dwarf> by pallas@eng.sun.com (Joseph Pallas)
|> quoted by boissier@irisa.fr (franck boissiere) in
|> <1991Mar26.101051.29527@irisa.fr>:
|> 
|> > > |> Sooner or later, opponents of static typing bring out the
|> > > |> heterogeneous collection.  
|> 
|> To handle heterogeneous collections without endangering static
|> typing, use genericity (Eiffel syntax):
|> 
|> 	class COLLECTION [G] ...
|>
|> [More details about Eiffel's generic classes]


Yes, genericity *is* useful (and needed), but it is only a partial
solution.  At the risk of repeating myself, I'll restate the problem
which originally started this discussion:

-------  Hit 'n' if you've already seen this -------------

Suppose you have two classes T and SubT, where SubT is a subtype of T
(in the strict sense of `subtype').  You also have the lists

listA: List[A] 
listB: List[B]

(where List[T] is a generic List including the method insert(elem: T))

Also suppose that there exists a procedure someProc(l: List[A]) which
(among other things) may invoke the function a() on some elements of the
list (a() is defined in A).

Now the problem: List[T] is not a subtype of List[SubT] because of the
insert method (contravariance).  Thus, someProc(listB) is illegal even
though it is perfectly safe since all of its elements are guaranteed to
understand a().  

Summary: instances of generic types are rarely subtypes of each other.
Thus, code which takes an instance of a generic type as an argument cannot
be reused for another instance of this generic type.  This can seriously 
restrict reusability in programs which use many related instances of generic
types, e.g. collections.

------------------------------------------------------------------------------
Urs Hoelzle                 			       hoelzle@cs.stanford.EDU
Center for Integrated Systems, CIS 42, Stanford University, Stanford, CA 94305

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (03/28/91)

In article <1991Mar27.042219.18924@leland.Stanford.EDU> hoelzle@elaine13.Stanford.EDU (urs hoelzle) writes:
> Really.   I'm not sure I understand your solution, so could you please 
> post it?

What list functions do you want? Most of the common ones are spelled out
adequately in Knuth. You can translate from there directly into C.

> Do you really want an "inlined" copy of that code at every 
> call site?

No, so you hide the longer pieces behind function calls.

---Dan

bertrand@eiffel.UUCP (Bertrand Meyer) (03/29/91)

From <1991Mar28.030354.25051@leland.Stanford.EDU> by
hoelzle@leland.Stanford.EDU [Order of paragraphs reversed]:

> Suppose you have two classes T and SubT, where SubT is a subtype of T
> (in the strict sense of `subtype').  You also have the lists
> 
> listA: List[A] 
> listB: List[B]
> 
> (where List[T] is a generic List including the method insert(elem: T))
> 
> Also suppose that there exists a procedure someProc(l: List[A]) which
> (among other things) may invoke the function a() on some elements of the
> list (a() is defined in A).
> 
> Now the problem: List[T] is not a subtype of List[SubT] because of the
> insert method (contravariance).  Thus, someProc(listB) is illegal even
> though it is perfectly safe since all of its elements are guaranteed to
> understand a().  


> Yes, genericity *is* useful (and needed), but it is only a partial
> solution.

[The discussion below uses LIST rather than List and some_proc
rather than someProc.]

Here constrained genericity does provide a solution.
(Not necessarily *the* solution, but the best one that I know.)
The reasoning (as sketched in a previous posting) is that if
some_proc is to apply function a to objects of type T, it needs
some reassurance that such objects indeed have a function a.
It does not need to know what the function actually does, simply
that it exists, has a certain signature (argument and result types)
and, if so desired, certain semantic properties expressed by
a precondition and/or a postcondition.

The solution, then, is to introduce a deferred (i.e. abstract)
class SPECIFIC, with a deferred (not implemented) function a:

	deferred class SPECIFIC feature

		a: SOME_TYPE is deferred end;

		...
	end -- class SPECIFIC

to request that any actual generic parameter LIST be a
descendant of SPECIFIC:

	class LIST [T -> SPECIFIC] feature

		...

	end -- class LIST

and to make sure that A includes SPECIFIC among its parents.

Of course, this means that B to must be a descendant of SPECIFIC,
and Mr. Hoelzle's point is probably that this is inappropriate since
some_proc will only be called with arguments of type LIST [A]. However
the problem does not arise in this way in ``pure'' object-oriented
programming of the Simula/Eiffel (and, unless I am mistaken, Smalltalk) style.
In this world some_proc would not take an argument l of list type,
as in Mr. Hoelzle's statement of the problem given above, but
would be a procedure in a list class. Then two possibilities arise:

- Either some_proc appears in the generic class LIST as given above,
in which case it is legitimate to require that B be a descendant of
SPECIFIC since some_proc will be applicable to targets of type B.

- Or we do want some_proc to be applicable only to targets of type A.
Then we do not need constrained genericity; we should declare some_proc
not in the basic LIST class but in a descendant written specifically
for that purpose:

class LIST_OF_A inherit

	LIST [A]  

feature

	some_proc is
		local
			current_list_element: A
			x: SOME_TYPE
		do
			from ... loop ...

				x := current_list_element.a  --<------

			end
			...
		end -- some_proc

end -- class LIST_OF_A
				

In the call marked --<-----, the application of function `a' to
`current_list_element' is typewise valid since the function is assumed
to be a feature of class A.


In summary, static typing means that the software text must
make explicit the assumptions about what operations are applicable
to what entities, and then must not go beyond these assumptions in
applying operations to entities.

-- 
-- Bertrand Meyer
bertrand@eiffel.uucp

boehm@parc.xerox.com (Hans Boehm) (03/29/91)

hoelzle@leland.Stanford.EDU (urs hoelzle) writes:

[Me:]
>> As someone who hasn't yet completely converted to the object oriented religion,
>> this has always struck me as using the wrong tool for the job.  I really don't
>> want a "list of T or subtype of T".  I want a "list of objects that understand
>> foo".  Once I take that view, I have no problems, at least in the few
>> statically typed languages that allow this (e.g. Russell, and probably FX from
>> MIT, and probably Scratchpad II from IBM).  I also don't need to construct
>> my types in such a way that all types that understand foo are "derived" from
>> a common supertype;  I can use inheritance purely where it simplifies the
>> implementation.  And I get full static checking, though elements of the list
>> will, of course, somehow carry the "foo" function with them.  (In Russell, I
>> would build a list of pairs <x: T, T: [algebra containing foo]>.)


>This sounds good.  However, I suspect that these languages don't (didn't?)
>have inheritance at all...?  Plus, some people don't like functional languages.
>Could you post some pointers to the relevant literature?

I'd be glad to.

An introduction to the ideas behind Russell is in Demers and Donahue,
"Data Types are Values", TOPLAS 7, 3 (1985), pp. 426-445.

A (the) Russell implementation is available by anonymous ftp from
arisia.xerox.com:~ftp/russell.tar.Z.  It includes some further documentation.

There is a paper by Jouvelot anf Gifford in the 1991 POPL Proceedings that
cites some of the earlier papers on FX.

None of these languages are purely functional.  They all contain assignment.
(I don't like purely functional languages either.)

As far as I know, these languages do not provide inheritance in the now
conventional sense.  (I know Russell does not.)  They often get a similar
effect by allowing the use of an algebra with many operations in a place
where few operations are called for.  But adding data fields (as opposed
to functions or methods) to existing algebra implementations can be a bit
clumsy.  (You can fake it by adding an extra field whose type is a parameter
to the implementation of the supertype, and instantiating that in the subtypes.)
There are ways to avoid this.  In the case of Russell, they were never
implemented.  I believe Cardelli's language "Quest" is flexible enough
to implement inheritance, and to express the above notions.  It is no doubt
described in a DEC SRC report, though I don't have the reference handy.


Hans
(boehm@xerox.com)

pallas@eng.sun.com (Joseph Pallas) (03/29/91)

In <1991Mar27.183206.5506@leland.Stanford.EDU>
hoelzle@leland.Stanford.EDU (urs hoelzle) writes:

>The problem is (unfortunately) harder than "const": if you have a generic
>type (say List), e.g.:

>type List[T] = 
>	......
>	procedure f(arg: T);

>then the above problem pops up, even if f doesn't change the List (e.g.
>"const arg: T" wouldn't change the problem a bit).  The problem is 
>contravariance: for a List[SubT], f's signature is "procedure taking a SubT",
>i.e. a more restricted type than T, violating contravariance.

We've strayed far afield from the topic of heterogeneous collections,
I'd say.  The problem of the relationship between List[T] and List[U]
when U is a subtype of T (which is definitely NOT a subtype
relationship) is actively being researched.  

This problem doesn't go away in dynamically typed languages.

joe

davidm@uunet.UU.NET (David S. Masterson) (03/29/91)

>>>>> On 26 Mar 91 23:17:31 GMT, peter@ficc.ferranti.com (Peter da Silva) said:

Peter> In article <26714:Mar2602:52:1891@kramden.acf.nyu.edu>
Peter> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:

Dan> So: What use can anyone make of a heterogeneous list?

Peter> Message queue for a real-time operating system?

Every message in the queue would be a member of the class Message, no?
Therefore, this would be a homogeneous queue.  As Chip said, next contestant.

;-)
--
====================================================================
David Masterson					Consilium, Inc.
(415) 691-6311					640 Clyde Ct.
uunet!cimshop!davidm				Mtn. View, CA  94043
====================================================================
"If someone thinks they know what I said, then I didn't say it!"

davidm@uunet.UU.NET (David S. Masterson) (03/29/91)

>>>>> On 26 Mar 91 10:10:51 GMT, boissier@irisa.fr (franck boissiere) said:

Franck> There is at least one kind of system which must allow heterogeneous
Franck> collections. It is often called Open System. Is these systems the
Franck> person who writes the kernel do not know anything about what objects
Franck> will be used by the persons who will extend the system.

What message would this OpenSystem send to the objects that it deals with?
What do those messages have in common?  If they have nothing in common, then
how does the OpenSystem even know what messages to send to these collections
of objects?

Franck> Another exemple is for debugging, where you might collect any kind
Franck> of objects.

Wouldn't it all be of type DebugInformation?  Therefore, same questions apply.

Next contestant.  (I like that phrase, Chip :-)
--
====================================================================
David Masterson					Consilium, Inc.
(415) 691-6311					640 Clyde Ct.
uunet!cimshop!davidm				Mtn. View, CA  94043
====================================================================
"If someone thinks they know what I said, then I didn't say it!"

davidm@uunet.UU.NET (David S. Masterson) (03/29/91)

>>>>> On 27 Mar 91 17:24:32 GMT, bertrand@eiffel.UUCP (Bertrand Meyer) said:

Bertrand> To handle heterogeneous collections without endangering static
Bertrand> typing, use genericity (Eiffel syntax):

Bertrand> 	class COLLECTION [G] ...

Is this a heterogeneous collection (one collection object containing many
objects of many types) or a generic collection class (a collection class that
may be defined to contain any *single* type of object [which may have multiple
parent types])?

Next contestant?
--
====================================================================
David Masterson					Consilium, Inc.
(415) 691-6311					640 Clyde Ct.
uunet!cimshop!davidm				Mtn. View, CA  94043
====================================================================
"If someone thinks they know what I said, then I didn't say it!"

anw@maths.nott.ac.uk (Dr A. N. Walker) (03/30/91)

In article <1991Mar25.220525.11087@leland.Stanford.EDU>
hoelzle@leland.Stanford.EDU (urs hoelzle) writes:

>	type T = "some type"
>	type SubT = "subtype of T"

	And here's where I start having difficulties.  I wanted to reply
to Urs's challenge using Algol 68 with the modal extensions (described by
Charles Lindsey in Algol Bulletin #37, July 1974).  But Algol doesn't
have subtypes;  you instead always build modes "upwards".  Well, perhaps
that's a problem with Algol;  but Urs's challenge is based on a particular
OO model, and it would help me to know whether I'm *really* missing out
on something if he [or some other kind reader] could re-state the challenge
in a more neutral way, eg as a real-world problem.

>				       someProc just iterates through the
>    list and sends 'foo'

	OO again!

>							      But as soon as
>    Lists are mutable (e.g. define insert(newElem: T)), the type system
>    won't let you do it because List[SubT] is *not* a subtype of List[T].

	Why not?  Because your particular OO language has this restriction?
Or is there [or do you think there is!] something deeper that makes this a
generic problem with static typing.

	I had other problems with Urs's challenge.  He wanted to construct
a list of "A" called "listA" and a list of "B" called "listB", and then to
concatenate them into a "listC".  But you can't concatenate lists of
different types -- that's a *strong* typing, not a *static* typing
restriction.  I can construct a "listC" from *copies* of the elements of
"listA" and "listB":

	mode list = (mode x) struct (x element, ref list (x) next);

	proc concatenate = (mode x, y, ref list (x) lx, ref list (y) ly)
				     ref list (union (x, y)):
		if lx isnt nil then list (union (x, y)) :=
			 (element of lx, concatenate (x, y, next of lx, ly))
		  elif ly isnt nil then concatenate (y, x, ly, lx)
		  else nil
		fi;

	ref list (a) lista := ...;
	ref list (b) listb := ...;
	ref list (union (a, b)) listc := concatenate (a, b, lista, listb);

[untested! -- no doubt someone will tell me if I've got the reference levels
wrong somewhere].  Is this what Urs wanted?  Or have I constructed something
quite other?

	Again, Urs wanted me to "send foo" to "listC" and to "display" the
elements.  The problem here is that, in Algol, any given identifier has a
statically determined meaning, so a procedure cannot [unless I've missed
something] decide at run-time which of several identically identified
procedures is really meant.  The following *is* possible:

	proc apply = (mode x, ref list (x) lx, op (ref x) void p) void:
	   ( lx isnt nil | p element of lx; apply (x, next of lx, p) );

	op jointdisplay = (union (a, b) this) void:
	   case this in
	       (a thisa): display thisa,
	       (b thisb): display thisb
	   esac;

	apply (union (a, b), listc, jointdisplay)

	comment
	  where "display" is an operator, and "a" and "b" are assumed to be
	  sufficiently different modes;  otherwise, they might as well all
	  be procedures with names like "displaya" and "displayb"
	comment

but this may not be sufficiently generic for everyone's tastes.  I'll
think about the "foo" problem, but again I suspect there is no completely
generic solution in Algol, and there must be different "fooa", "foob"
"objects".  This is, of course, where inheritance is nice;  but you do
then run into problems with multiple inheritance that have not, as far
as I know, been solved in an aesthetically satisfying [ie, better than
"this is what my compiler does about it"] way.

-- 
Andy Walker, Maths Dept., Nott'm Univ., UK.
anw@maths.nott.ac.uk

bertrand@eiffel.UUCP (Bertrand Meyer) (03/30/91)

From <CIMSHOP!DAVIDM.91Mar28192651@uunet.UU.NET> by
cimshop!davidm@uunet.UU.NET (David S. Masterson):

[Quoting from my earlier posting:]

   >> To handle heterogeneous collections without endangering static
   >> typing, use genericity (Eiffel syntax):

   >>       class COLLECTION [G] ...

[Mr. Masterson's reply:]

   > Is this a heterogeneous collection (one collection object
   > containing many objects of many types) or a generic collection
   > class (a collection class that may be defined to contain any
   > *single* type of object [which may have multiple parent types])?
   > 
   > Next contestant?

Although this is probably clear to most people reading this newsgroup,
it may be worth confirming that the collection described by the above
COLLECTION class is indeed heterogeneous (if so desired by
clients). If a client declares

   tax_collection: COLLECTION [TAX]

and then uses the routines of class COLLECTION to insert objects into
`tax_collection' and retrieve them, these objects may be direct instances
not just of class TAX but of any of its proper descendants.
(``Proper descendant'' means direct or indirect heir, according
to the inheritance relation.)

This is a direct result of the type rules: if COLLECTION [G]
has an insertion procedure

   put (new: G) is
         -- Add `new' to the collection
      do
         ...
      ensure
         has (new)
      end -- put

then in a call of the form

   tax_collection.put (tx)

the actual argument tx may be of any type which is a descendant of TAX.

In different calls, we may use actual arguments of different types,
inserting objects of different types into the same collection.
-- 
-- Bertrand Meyer
bertrand@eiffel.uucp

sommar@enea.se (Erland Sommarskog) (04/01/91)

Also sprach Urs Hoelzle (hoelzle@leland.Stanford.EDU):
>	type T = "some type"
>	type SubT = "subtype of T"
>
>	procedure someProc(c: List[T])
>
>    I think that you'll agree with me that you'd like to use someProc with
>    a Collection[SubT], especially if someProc just iterates through the
>    list and sends 'foo' -- after all, every SubT "is a" T.  But as soon as
>    Lists are mutable (e.g. define insert(newElem: T)), the type system
>    won't let you do it because List[SubT] is *not* a subtype of List[T].

In which type system? If you imply the type system in any statically
typed language, I have news for you. The Eiffel code below compiles 
and executes just fine. For those of you who are unacquainted with the
kernel library, I should add that STRING is an heir of COMPARABLE.
(I assume when you talk of subtype you mean something like an heir.)

CLASS Subtype

FEATURE

Some_proc(L : Linked_list[Comparable]) : boolean IS
REQUIRE L.count >= 2;
DO
  Result := L.first < L.last;
END;

CREATE IS
LOCAL L : Linked_list[String];
      X : boolean;
DO
  L.create;
  L.Put_left("First");
  L.Put_left("Last");
  X := Some_proc(L);
  Io.putbool(X);
END;

END;
-- 
Erland Sommarskog - ENEA Data, Stockholm - sommar@enea.se

carroll@cis.udel.edu (Mark Carroll) (04/01/91)

In article <27F11E1D.6354@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>According to objtch@extro.ucc.su.oz.au (Peter Goodall):
>>I have written a Smalltalk application which will unload fairly arbitary
>>recursive objects to an append only Stream (pipe).
>
>But not _totally_ arbitrary objects, because the objects so dumped
>must understand a dumpTo: method, right?
>
>C++ can easily solve this problem using multiple inheritance, virtual
>functions, and vanilla homogenous lists.
>
>Next contestant?


This article and its replies made me a bit angry. There's a rather huge
degree of ignorance in them.

All effective programming systems are equivalent. Anything that I can do
in ANY language, I can do in any other language.

There is absolutely no way that I can make an argument that a dynamic
type system can do something that can't be done in a static system -
because anything that I can do in language A, I can do in language B.

Cutting down the arguments for dynamic typing because you can do the
same job with static typing are vacuous. Obviously you can! No one has
ever claimed that you couldn't! The question is: which does it BETTER?

The argument shouldn't be:
  "I can do heterogeneous lists in a static system (using MI), so
   since you don't need dynamic typing to do it, dynamic typing is
   no good".

The correct form of the argument is this:
  "My staticly typed solution is a BETTER solution than your
   dynamic one, and this is why:".

Now, in the context of this argument, I think that the dynamic solution
under which I can iterate ANY method over a heterogeneous lists is a
better, cleaner solution that the static solution in which I have to
create an artificial superclass for every method that I want to use
to iterate over the list.

Disagree? Please, tell me why.

>Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>


	<MC>
-- 
---------------------- Mark Craig Carroll:  <MC> ------------------------
------ U of Del. Grad Student in CIS ------ EE/CIS LabStaff Hacker ------
-- Resident Weirdo @ 105 E Cleveland Ave, Newark DE - carroll@udel.edu --
---------------------- Shalom Achshav - Peace NOW! ----------------------

zeil@cs.odu.edu (Steven J. Zeil) (04/01/91)

In article <49413@nigel.ee.udel.edu> carroll@cis.udel.edu (Mark Carroll) writes:
>This article and its replies made me a bit angry. There's a rather huge
>degree of ignorance in them.
>
>All effective programming systems are equivalent. Anything that I can do
>in ANY language, I can do in any other language.
>
>There is absolutely no way that I can make an argument that a dynamic
>type system can do something that can't be done in a static system -
>because anything that I can do in language A, I can do in language B.
>


The "all languages are equivalent power" argument applies only within
the context of a single program execution.  It does not apply when we
begin to discuss the evolution of a set of programs as code is re-used
and especially as as new programs manipulating previously-defined
objects are conceived and implemented.

The original poster was discussing the ability of a dynamically typed
language to support a common I/O protocol.  Yes, I can support his
protocol easily in a static type system  --IF-- I know all of the object
classes ahead of time or am willing to rewrite portions of the I/O
protocol each time that I define a new class.   Otherwise, the
original poster is correct in claiming that it "can't be done" over
the history of the development of a related set of programs.

                                                                Steve Z

peter@ficc.ferranti.com (Peter da Silva) (04/02/91)

In article <CIMSHOP!DAVIDM.91Mar28190551@uunet.UU.NET> cimshop!davidm@uunet.UU.NET (David S. Masterson) writes:
> Every message in the queue would be a member of the class Message, no?

That's an implementation detail. Really. The programming model is a queue
you can stick varying objects into... how they're encapsulated so the guy
at the other end can recover the type information is beside the point.
-- 
Peter da Silva.  `-_-'  peter@ferranti.com
+1 713 274 5180.  'U`  "Have you hugged your wolf today?"

hoelzle@neon.Stanford.EDU (Urs Hoelzle) (04/02/91)

sommar@enea.se (Erland Sommarskog) writes:

>Also sprach Urs Hoelzle (hoelzle@leland.Stanford.EDU):
>>	type T = "some type"
>>	type SubT = "subtype of T"
>>
>>  [ List[SubT] is *not* a subtype of List[T] ]

>In which type system? If you imply the type system in any statically
>typed language, I have news for you. The Eiffel code below compiles 
>and executes just fine. 
> [Eiffel code]

It is a well-known fact that Eiffel does not strictly enforce
contravariance, and it is equally well-known that there are (very
simple) programs which type-check in ISE's implementation but bomb at
runtime because of a type error [programs available on request; you
can find them yourself in the ECOOP'89 proceedings].  Thus, Eiffel (in
its current implementation) is not a statically-typed language because
there may be run-time type errors.

There are two points that I should mention in Eiffel's defense:

1) Last year, Bertrand Meyer described a solution to the current type
   holes sometime last year.  The newest Eiffel release might
   implement this.

2) Eiffel does indeed try to overcome the restrictions of
   contravariance (which is basically what I'm calling for), but it
   does it by totally abolishing contravariance, and I'm not sure this
   is the right way to go.

   Furthermore, the proposed fix to Eiffel's type holes is very
   strict.  Informally speaking, you can have either assignments of
   the form aListOfComparable := aListOfString OR calls of
   aListOfComparable.append(aComparable) *in your entire system*.
   Since any non-trivial system (which actually uses covariance) will
   have both, the program will not type-check and we're back to
   contravariance.
   
So: either Eiffel isn't type-safe, or any actual use of covariance in
a realistic program will not type-check.  Or, to put it differently:
in *any* type system, one of the following two assertions is true:

(I)  List[SubT] is not a subtype of List[T] (i.e. you cannot *always*
     substitute a List[SubT] for a List[T]).

(II) The type system is safe, i.e. there will be no run-time type errors
     if a program passes the type checking.

Since (at least in the context of this discussion) we're only
interested in type-safe languages where (II) is false, it follows that
(I) must be true.

-Urs
   
-- 
------------------------------------------------------------------------------
Urs Hoelzle                                            hoelzle@cs.stanford.EDU
Center for Integrated Systems, CIS 42, Stanford University, Stanford, CA 94305

davidm@uunet.UU.NET (David S. Masterson) (04/02/91)

>>>>> On 30 Mar 91 01:41:37 GMT, bertrand@eiffel.UUCP (Bertrand Meyer) said:

> From <CIMSHOP!DAVIDM.91Mar28192651@uunet.UU.NET> by
> cimshop!davidm@uunet.UU.NET (David S. Masterson):

> [Quoting from my earlier posting:]

>   >> To handle heterogeneous collections without endangering static
>   >> typing, use genericity (Eiffel syntax):

>   >>       class COLLECTION [G] ...

> [Mr. Masterson's reply:]

>   > Is this a heterogeneous collection (one collection object
>   > containing many objects of many types) or a generic collection
>   > class (a collection class that may be defined to contain any
>   > *single* type of object [which may have multiple parent types])?

> Although this is probably clear to most people reading this newsgroup,
> it may be worth confirming that the collection described by the above
> COLLECTION class is indeed heterogeneous (if so desired by
> clients).

I really hate the idea of contradicting Bertrand Meyer as I'm probably getting
in over my head.  However, I think there is a point that is being missed (and
I'm probably the one missing it).  I believe the original poster was looking
for a use for a *truly* heterogeneous collection.  From Mr. Meyer's
description of COLLECTION (I don't know Eiffel), it still seems that this is
more of a generic collection class in that all objects that can be contained
in the collection are related through some (base) type and, therefore, follow
some (base) protocol.  Obviously, both C++ (through pointers to base class and
polymorphism) and Smalltalk (because everything is ultimately an Object) have
analogues to this.  However, I think what is desired is something where
objects in the collection are not related and, therefore, follow no set
protocol.  For instance, in the following:

	tax_collection: COLLECTION [TAX]

users of the collection know that (at the very least) all objects obtained
from the collection will follow the TAX object protocol.  On the other hand:

	h_collection: COLLECTION [void]		/* or is it 'void*'? */

might be a *truly* heterogeneous collection (assuming objects entered into the
collection can be implicitly cast to VOID), but (going back to the original
question) how useful would it be since all clues as to type and protocol of
objects in the collection are removed?
--
====================================================================
David Masterson					Consilium, Inc.
(415) 691-6311					640 Clyde Ct.
uunet!cimshop!davidm				Mtn. View, CA  94043
====================================================================
"If someone thinks they know what I said, then I didn't say it!"

edwardj@microsoft.UUCP (Edward JUNG) (04/03/91)

In article <CIMSHOP!DAVIDM.91Mar28191355@uunet.UU.NET> cimshop!davidm@uunet.UU.NET (David S. Masterson) writes:
>>>>>> On 26 Mar 91 10:10:51 GMT, boissier@irisa.fr (franck boissiere) said:
>
>Franck> There is at least one kind of system which must allow heterogeneous
>Franck> collections. It is often called Open System. Is these systems the
>Franck> person who writes the kernel do not know anything about what objects
>Franck> will be used by the persons who will extend the system.
>
>What message would this OpenSystem send to the objects that it deals with?
>What do those messages have in common?  If they have nothing in common, then
>how does the OpenSystem even know what messages to send to these collections
>of objects?
>

No, you are missing the point. An open system would interface with objects
that conform to a certain interface. The notion in an "in the large"
environment is that conformance is independent of the inheritance
hierarchy -- that is, the conforming objects do not have to share the
same inheritance graph or subclass from the same object, rather they
implement the same protocol.

This is encapsulation, an important and oft-overlooked aspect of
object orientation (or object basis, depending upon your nomenclature).

If I want to build an interface to a spell checker such that anyone
could write a conforming spell checker, I write to an interface
(or protocol, depending upon your terminology). This way I can send
the right messages to the object without knowing about it (the
thing is "type safe" at compile time, but would involve a run-time
assertation if defensively programmed), and the implementation of
that object is independent of the inheritance graph.

Such interface or protocol-based implementations are not supported
by most popular object-oriented languages, although some people
are considering extensions to do so.

This is part of the whole "inheritance breaks encapsulation" notion.


--
Edward Jung
Microsoft Corp.

My opinions do not reflect any policy of my employer.

richieb@bony1.bony.com (Richard Bielak) (04/04/91)

In article <CIMSHOP!DAVIDM.91Apr1224716@uunet.UU.NET> cimshop!davidm@uunet.UU.NET (David S. Masterson) writes:
[...]
>
>I really hate the idea of contradicting Bertrand Meyer as I'm probably getting
>in over my head. 

Don't blame you! :-)
[...]

>On the other hand:
>
>	h_collection: COLLECTION [void]		/* or is it 'void*'? */
>
>might be a *truly* heterogeneous collection (assuming objects entered into the
>collection can be implicitly cast to VOID), but (going back to the original
>question) how useful would it be since all clues as to type and protocol of
>objects in the collection are removed?

The generic collection should really be declared as:

        h_collection: COLLECTION [ANY];

as any class in Eiffel is an heir of ANY.

The following trick can be used to do things with objects in such
a collection.

        my_weird_object : MY_WEIRD_CLASS;

        ....

        my_weird_object ?= h_collection.next_item;

        -- above line gets the next item from the collection
        -- and does a "reverse" assigment to my variable.
        -- this assigment will only work, if the type on
        -- the right is of type MY_WEIRD_CLASS (or a descendant)
        -- otherwise, Void is returned. 
        -- so,

        if not my_weird_object.Void then
                -- got a weird_object_here, do what you need to
                ....
        end;



...richie "What does a virtual void look like?"

-- 
*-----------------------------------------------------------------------------*
| Richie Bielak  (212)-815-3072    | Programs are like baby squirrels. Once   |
| Internet:      richieb@bony.com  | you pick one up and handle it, you can't |
| Bang:       uunet!bony1!richieb  | put it back. The mother won't feed it.   |

ericco@ssl.berkeley.edu (Eric C. Olson) (04/04/91)

In the article Richard Bielak writes:
>   The generic collection should really be declared as:
>
>	   h_collection: COLLECTION [ANY];
>
>   as any class in Eiffel is an heir of ANY.

I read an interesting paper that included a discussion of
heterogeneous objects in an object-oriented database.

The basic idea was to resolve the class heirarchy as late as
possible, but during query semantic checking.  That is, given
a heterogeneous set of objects to work on, is it legal to
apply a particular method on all of the objects?

The set of classes (and superclasses) that define the objects are
inspected.  For each class, the method is resolved (in it or its
superclasses).  If this method exist in each of the classes,
and has the same arity and type, then the query is semantically valid.

To me, this seems to be a runtime class definition.  For this task,
you are defining a runtime superclass with a single method.  The
generalization would be, given any set of objects, define the
runtime superclass which is the intersection of each object's methods
(based on arity and type).  This is guaranteed to at least be the
root class.

Hmmm,
Eric
--
Eric
ericco@ssl.berkeley.edu

sakkinen@jyu.fi (Markku Sakkinen) (04/05/91)

In article <ERICCO.91Apr4095619@sdaf1.ssl.berkeley.edu> ericco@ssl.berkeley.edu (Eric C. Olson) writes:
> ...
>I read an interesting paper that included a discussion of
>heterogeneous objects in an object-oriented database.
>
>The basic idea was to resolve the class heirarchy as late as
>possible, but during query semantic checking.  That is, given
>a heterogeneous set of objects to work on, is it legal to
>apply a particular method on all of the objects?
> ...

You probably mean "Type Consistency of Queries in an Object-Oriented
Database System" by Straube and Ozsu (O"zsu), OOPSLA/ECOOP'90.

Reply-To: sakkinen@jytko.jyu.fi (Markku Sakkinen)
Markku Sakkinen
Department of Computer Science and Information Systems
University of Jyvaskyla (a's with umlauts)
PL 35
SF-40351 Jyvaskyla (umlauts again)
Finland
          SAKKINEN@FINJYU.bitnet (alternative network address)