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
new@ee.udel.edu (Darren New) (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. Hmm... How about a window with a hetrogeneous collection of buttons, sliders, text displays, etc, all of which respond to "redraw" and "is the mouse over you"? How about in GROPE (my project) in which each window has a hetrogeneous collection of graphical Estelle objects (modules, channels, states, transitions, queues, etc) which all respond to "redraw", "scale to parent", "select good default graphics", "is the mouse over you", and so on? I have also done simulation code where collections of hetrogenous objects are in "shopping carts" which all respond to "what is your price" and such things. Naturally, if all you have is a hammer, you never see the need for a screw. -- Darren -- --- Darren New --- Grad Student --- CIS --- Univ. of Delaware --- ----- Network Protocols, Graphics, Programming Languages, Formal Description Techniques (esp. Estelle), Coffee, Amigas ----- +=+=+ My time is very valuable, but unfortunately only to me +=+=+
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
cs450a03@uc780.umd.edu (03/26/91)
Darren New writes: >Hmm... How about a window with a hetrogeneous collection of buttons, >sliders, text displays, etc, all of which respond to "redraw" and "is >the mouse over you"? The way I look at things, I'd probably think of that as a homogenous collection. It's only when you get to taking the things apart that you can really see any distinction between them. (Or maybe, when you try to pass a paragraph of text through a slider.) Of course, I suppose that's a dynamic typing viewpoint... *shrug* Raul Rockwell
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
new@ee.udel.edu (Darren New) (03/27/91)
In article <25MAR91.22422777@uc780.umd.edu> cs450a03@uc780.umd.edu writes: >Darren New writes: >>Hmm... How about a window with a hetrogeneous collection of buttons, >>sliders, text displays, etc, all of which respond to "redraw" and "is >>the mouse over you"? > >The way I look at things, I'd probably think of that as a homogenous >collection. It's only when you get to taking the things apart that >you can really see any distinction between them. (Or maybe, when you >try to pass a paragraph of text through a slider.) Hmm... a homogenous collection that when you take it apart yields things with different types. Somehow, I think we are confused over terminology. Either that, or you are reaching for straws :-) The whole point of a hetrogeneous collection is that sometimes you treat everything the same and sometimes you treat each thing differently. Let's call a "hetrogeneous collection" something that holds multiple values of multiple types and over which some code iterates (hence the "collection" part) and over which other code applies different functions depending on the type (hence the "hetrogeneous" part). If this isn't a good enough definition, then all I can say is "When you drive screws with a hammer, *nothing* looks like a screwdriver." In the above case, the routine to refresh the screen will iterate over the entire collection, while the routine to actually do the redrawing (which clearly must be dynamically bound) will do different things based on the type (slider, button, ...) of the object. Looks like a hetrogeneous collection to me. Note that this definition rules out a bunch of things which I might call hetrogeneous collections. For example, the arguments to a C function, which I've never iterated over in spite of it being a collection and of hetro types. The same goes for argv[] in "cp -i a.out /bin", in which argv[0] is an executable, argv[1] is a control flag, argv[2] is a file, and argv[3] is a directory. This is kind of iffy, because some programs iterate over some or all of argv[] (like "echo") and others never iterate over argv[]. However, in AmigaDOS under the Workbench (i.e., the icon-based command interpreter), all four of these parameters would be passed separately and in different places (not argv[]); this leads me to believe that argv should be considered a hetro collection. (Note that in AmigaDOS, there is another type for argv[], namely file wildcard. This goes back to the globbing thread. :-) Take, for another example, PostScript procedures: { /moveit { 2 0.5 moveto } def } This is a procedure array (procedures are arrays in ps) containing a literal name, an array, and an executable name. The inner array contains an integer, a float, and an executable name. If bound, the executable names become operators. The "inner interpreter" iterates over the elements of the array and either pushes or calls, based on the type of the object. Looks like a hetrogeneous collection to me. What about the heap in C? Malloc iterates over the allocated blocks while seeking a free one, while other code indexes different blocks differently. Looks like a hetro collection to me. Memory itself is a hetrogeneous collection of integers, floats, pointers to code, pointers to data, etc. Block moves iterate over it, other instructions treat it differently depending on the instruction. If you pass the wrong type of value to an instruction, you get garbage out. !file /bin/a* /bin/acctcom: mc68020 demand paged dynamically linked executable /bin/adb: mc68020 demand paged dynamically linked executable /bin/addbib: mc68020 demand paged dynamically linked executable /bin/adjacentscreens: symbolic link to sunview1/adjacentscreens /bin/aedplot: mc68020 demand paged dynamically linked executable /bin/align_equals: symbolic link to sunview1/align_equals /bin/ar: mc68020 demand paged dynamically linked executable /bin/arch: executable shell script /bin/as: mc68020 demand paged dynamically linked executable /bin/at: mc68020 demand paged dynamically linked set-uid executable /bin/atoplot: mc68020 demand paged dynamically linked executable /bin/atq: mc68020 demand paged dynamically linked set-uid executable /bin/atrm: mc68020 demand paged dynamically linked set-uid executable /bin/awk: mc68020 demand paged dynamically linked executable Sure looks like "file" (or the shell) iterated over /bin. Sure looks like execv() is going to do different things depending on what element of the collection is passed to it. Before you convince me that directories are not hetrogeneous, you'll have to explain what EISDIR and ENOTDIR mean other than "dynamic type mismatch error". !cc -O2 main.c special.s prev.o lib.a Sure looks to me like a hetro list of file types passed as argv[]. The first is an integer parameter, the second is a C source, etc. Here, the "inner interpreter" selects based on first and last characters of argv[] to determine what to do. !ls ~new/tex/thesis biblio.bib darren.bst figures makeheads makeps outline.txt psfig.aux thesis.aux thesis.bbl thesis.blg thesis.dvi thesis.loe thesis.lof thesis.log thesis.ps thesis.tex thesis.toc Hmmm.... Looks like that directory has a hetrogeneous collection of bibliography files, bibliography styles, DVI files, lists of figures, log file, postscript files, tex files, etc. If I passed them all to lpr, do you think the same filter would be applied to each? printf("line %d percent complete %f file %s", ii, ff, ss); Sure looks like printf iterates over its arguments. Sure looks like the arguments are not all the same type. Isn't there an entire standard header file to handle hetrogeneous argument lists? C'mon people. I can't believe that you never used a hetrogeneous collection. I can't believe Dan B even tried to think of where he might have used a hetrogeneous list; he's too smart to have missed all these examples. Dynamic binding goes hand-in-hand with hetrogeneous collections. Any time you use or implement dynamic binding, you probably have a hetrogeneous collection in there somewhere, or you could have used static binding. -- Darren -- --- Darren New --- Grad Student --- CIS --- Univ. of Delaware --- ----- Network Protocols, Graphics, Programming Languages, Formal Description Techniques (esp. Estelle), Coffee, Amigas ----- +=+=+ My time is very valuable, but unfortunately only to me +=+=+
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)
cs450a03@uc780.umd.edu (03/27/91)
Darren New >> > or > Me >> >> >Hmm... How about a window with a hetrogeneous collection of buttons, >> >sliders, text displays, etc, all of which respond to "redraw" and "is >> >the mouse over you"? >>The way I look at things, I'd probably think of that as a homogenous >>collection. >Hmm... a homogenous collection that when you take it apart yields things >with different types. Somehow, I think we are confused over terminology. >Either that, or you are reaching for straws :-) The whole point of >a hetrogeneous collection is that sometimes you treat everything >the same and sometimes you treat each thing differently. Well.. my view is "type is a form of information", and you seem to be agreeing with me. For instance, consider a list of numbers: (0, 1, 2, 3, 4). Now consider a function, "f" which rounds a number up to the nearest even integer. Applying f to that list would yield (0, 2, 2, 4, 4). Voila! Hetrogenous data. (-: I believe you said "The whole point of a hetrogenous collection is that sometimes you treat everything the same and sometimes you treat each thing differently.") More generally, if your language allows you to encapsulate data, so that the encapsulated information is invisible to the user, then the type of that information should be invisible (until you "take it out of its box"). Anything less is not true encapsulation. At least, that's the way I look at things... >Let's call a "hetrogeneous collection" something that holds >multiple values of multiple types and over which some code iterates >(hence the "collection" part) and over which other code applies >different functions depending on the type (hence the "hetrogeneous" >part). Heh.. but type is just information, in my opinion. By the way, the way I'd write code to deal with the original challenge would be something like this: c =. a, b foo c display c or should that be c =. a, b display c foo c (I don't remember the particulars) Without some more detail on what the pieces were, I hesitate to write "foo" and "display". As a first cut though, I could make foo be an identity function, and display simply dump the data to standard output. >If this isn't a good enough definition, then all I can say is >"When you drive screws with a hammer, *nothing* looks like a >screwdriver." But I'm using neither a screwdriver nor a hammer. I'm using a computer ;-) >In the above case, the routine to refresh the screen will iterate over >the entire collection, while the routine to actually do the redrawing >(which clearly must be dynamically bound) will do different things >based on the type (slider, button, ...) of the object. Looks like a >hetrogeneous collection to me. It's ok. If I was writing the routine to actually do the redrawing, I'd probably think it was a hetrogenous collection too. And if you've seen some of my other postings, you might get the idea that I consider dynamic binding (of both functions and data) as pretty fundamental in general. Raul Rockwell
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?"
brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (03/27/91)
In article <48805@nigel.ee.udel.edu> new@ee.udel.edu (Darren New) writes: > Hmm... How about a window with a hetrogeneous collection of buttons, sliders, > text displays, etc, all of which respond to "redraw" and "is the mouse > over you"? struct { any object; void (*redraw)(); int (*ismouseover)(); } *whatsinwindow(); Here ``any'' is a polymorphic type (e.g., void * in C), and *redraw takes an ``any'' argument. Naturally, languages like C++ encapsulate the above struct into a single ``class,'' but that's merely syntactic sugar. ---Dan
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?"
adam@visix.com (03/28/91)
In article <48805@nigel.ee.udel.edu> new@ee.udel.edu (Darren New) writes: >- Hmm... How about a window with a hetrogeneous collection of buttons, sliders, text displays, etc, all of which respond to "redraw" and "is the mouse over you"? >- In article <7689:Mar2623:28:5091@kramden.acf.nyu.edu>, brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes: >- struct { any object; void (*redraw)(); int (*ismouseover)(); } *whatsinwindow(); Here ``any'' is a polymorphic type (e.g., void * in C), and *redraw takes an ``any'' argument. Naturally, languages like C++ encapsulate the above struct into a single ``class,'' but that's merely syntactic sugar. >- Let me get this straight. To construct a heterogeneous list of M items each guaranteeing N operations, I need to waste space for M*N function pointers? Yes, we need to store ONE pointer to each function, but the above scheme wastes space if we have already stored that pointer, e.g. in a different kind of heterogeneous list. A large windowing application uses hundreds of different lists. And I need to write an explicit constructor for every kind of list? You can do better than that, even in C. Adam
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
new@ee.udel.edu (Darren New) (03/28/91)
In article <26MAR91.19392004@uc780.umd.edu> cs450a03@uc780.umd.edu writes: >>Hmm... a homogenous collection that when you take it apart yields things >>with different types. Somehow, I think we are confused over terminology. >Well.. my view is "type is a form of information", and you seem to be >agreeing with me. Well, heck, cowboy. Of course type is a form of information. But is it information only the compiler has, or is it information that comes into play at run-time. The question is essentially whether variables have types or not. If not, then you have dynamic typing. If so, then you have static typing, because each particular reference (variable, expression, etc) has a known type at compile time. Of course if you can apply different functions to different elements of the list based on their type, then "type" is a kind of information you can access, whether that information is accessed only by the runtime system or whether it is directly accessible to the programmer. When you say something is a homogenous collection, you seem to be saying that every element is a <type,value> pair and hence is all the same type. Naturally in a dynamically typed system, the types have to be carried along with the values in some way. However, variables and expressions (in this case, expressions accessing elements of the list) have different <type> tags. The fact that the type of the first element of <type,value> is of a fixed type does not mean that the elements all have the same type (did that make any sense?). Take, for example, Smalltalk. You can say "it's statically typed, because every variable and every value is an object." This, of course, is useless. Why? Because when you only have one type, the static/dynamic distinction becomes irrelevant. (I believe this is actually the case in older lisps, where everything was a cons cell.) Any Smalltalk programmer is going to think of the class of an object as its type. Since variables can hold objects from different classes and since expressions can return objects of different classes, Smalltalk is dynamically typed. Naturally, any object's class is going to be an instance of Behavour, so it will repond to anything Behavour's instances respond to. Hence, the type of a type (i.e., class) is always of a specific type (namely, Behavour). This does not alter the dynamicly-typed nature of Smalltalk. Arguing this from the wrong viewpoint is pointless, as at the lowest level, the only type in computer systems is the bit (and not even that, if you talk to an EE person). At the highest level (e.g., Vice President of Marketing), the only type is "information." Since most programmers work somewhere inbetween these two levels, it is necessary to pick the right level of abstraction for discussing types. (Kind of like saying "The halting problem is solvable in practice. All you have to do is treat every bit of storage ever accessed by the CPU running your program as a giant state machine." Nobody ever thinks about the problem this way, and nobody ever programs a computer as a 2^{8gig}-state finite state machine, so such a statement, while true, is pretty much useless.) At the lowest level, the machine can treat the <type> tag as an integer or pointer or whatever. However, that isn't usually how the programmer treats it. If you want to discuss various forms of dynamic typing, we would need a different level of abstraction than if we are discussing dynamic vs. static typing. -- Darren -- --- Darren New --- Grad Student --- CIS --- Univ. of Delaware --- ----- Network Protocols, Graphics, Programming Languages, FDTs ----- +=+=+ My time is very valuable, but unfortunately only to me +=+=+ + When you drive screws with a hammer, screwdrivers are unnecessary +
new@ee.udel.edu (Darren New) (03/28/91)
In article <7689:Mar2623:28:5091@kramden.acf.nyu.edu> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes: >In article <48805@nigel.ee.udel.edu> new@ee.udel.edu (Darren New) writes: >> Hmm... How about a window with a hetrogeneous collection of buttons, sliders, >> text displays, etc, all of which respond to "redraw" and "is the mouse >> over you"? > >struct { any object; void (*redraw)(); int (*ismouseover)(); } *whatsinwindow(); > >Here ``any'' is a polymorphic type (e.g., void * in C), and *redraw >takes an ``any'' argument. Naturally, languages like C++ encapsulate the >above struct into a single ``class,'' but that's merely syntactic sugar. Am I crazy, or isn't (void *) impossible to indirect? Don't you have to type-cast it first? Isn't this dynamic typing implemented on top of C? I never said that C does not Bernstein-have dynamic typing. All I said was that C doesn't have dynamic typing. (Actually, I probably didn't say that either, but I do believe it.) "void *" is not a polymorphic type; it is a pointer type. "object" is not dynamically typed; it is of type "any" (i.e., void *). The fact that you can typecast it does not make it a dynamic type, Dan. It merely means that the designers of C realised that they needed some way of allowing you to implement dynamic typing on top of C. But then, that's the Bernstein-has form of the word "has." Please, in my future posts, assume that I am using the English version of the word "has." Thank you. Dan, do you really think I'm so stupid that I don't know how to implement dynamic typing on top of a statically typed language? Do you really think that I, like you, willfully misinterpret what is being said to support a different point of view without trying either to make a point or learn something from somebody else? Do you really think I would have given the above example as a hetrogenous list if it could be represented in one line of code in a statically typed language? Of course, C is completely useless because it's all merely syntactic sugar for large finite state machines. That's how we can solve the halting problem for it. Of course, since every language is merely syntactic sugar for large finite state machines, discussing advantages and disadvantages of different languages is moot -- just write a macro expander. Actually, all computers are analog computers. Binary is just syntactic sugar on top of volatage differences. Since we can't have *real* digital computers, but merely illusions of them, then we can't solve the halting problem in the way you earlier proposed anyway. For any discussion, a level of detail and abstraction can be chosen which renders the discussion pointless. Thank you for finding that level in this particular discussion, so we might avoid it in the future. -- Darren -- --- Darren New --- Grad Student --- CIS --- Univ. of Delaware --- ----- Network Protocols, Graphics, Programming Languages, FDTs ----- +=+=+ My time is very valuable, but unfortunately only to me +=+=+ + When you drive screws with a hammer, screwdrivers are unnecessary +
cs450a03@uc780.umd.edu (03/28/91)
Darren New writes: >Well, heck, cowboy. Of course type is a form of information. But is >it information only the compiler has, or is it information that comes >into play at run-time. The question is essentially whether variables >have types or not. If not, then you have dynamic typing. If so, then >you have static typing, because each particular reference (variable, >expression, etc) has a known type at compile time. Variables have types when I assign them values. That information is carried explicitly in interpreted code, and implicitly in compiled code. I don't have to variable declare types for the compiler any more than I have to declare the types of intermediate results. Of course, for ill-conditioned cases, the compiler generates lousy code. The simple solution is don't write bad code. >When you say something is a homogenous collection, you seem to be >saying that every element is a <type,value> pair and hence is all the >same type. Naturally in a dynamically typed system, the types have to >be carried along with the values in some way. Did I say that? hmm... If I have a bitmap, that's a homogenous collection, and I sure don't carry type information along with each bit. On the other hand, if for some reason I wanted a collection of bitmaps and strings, I'd have a list of pointers to data descriptors. One pointer (and descriptor) for each element of the list. Those data descriptors would carry "type" [and size], etc. But I deal with a homogenous collection: a list of pointers. Personally, if I have bitmaps and strings, I'd prefer to refer to them by separate names (that is, store them in separate variables). And as a general rule, this runs faster (than using indirection). Raul Rockwell
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
brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (03/28/91)
In article <1991Mar27.164605.24547@visix.com> adam@visix.com writes: > Let me get this straight. To construct a heterogeneous list of M > items each guaranteeing N operations, I need to waste space for M*N > function pointers? No, because you can just have a single ``class'' structure with the N function pointers, and keep just one pointer per ``object.'' But this extra layer is pointless for small problems, and I didn't bother putting it in. > And I need to write an explicit constructor for every kind of list? That depends on what you mean by ``explicit.'' If you have several types each supporting some sort of redraw operation, and you want to set up a list of redrawable things, then you do have to say the correspondence between ``redrawable'' and the redrawing for each type. Other posters have mentioned how in object-oriented languages you can make the redrawing class either a superclass or a subclass of all the classes in question; either way provides the necessary correspondence, but I wouldn't say it's explicit. ---Dan
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
brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (03/28/91)
In article <49087@nigel.ee.udel.edu> new@ee.udel.edu (Darren New) writes: > Am I crazy, or isn't (void *) impossible to indirect? Don't you have > to type-cast it first? Isn't this dynamic typing implemented on top of > C? YES. People are tossing around the term ``dynamic typing'' as if it were some truly important feature. ``Yeah, Bob, not only does the language have *loops*, but it has *dynamic typing*!'' ``Wow! *Dynamic typing*? Really?'' Dynamic typing isn't a semantic feature. It's a state of mind. If you want to use the pair (type,value) as a value with certain constraints, you can, whether the language supports it or not. It takes at most a good preprocessor to make this just as convenient as in any ``dynamically typed'' language. I accept that people want to use dynamic typing now and then. Fine! What I'm reacting to is this delusion that you need dynamic typing in the language to use it conveniently. Sure, I use associative arrays now and then, and I have an associative array library for the job. Why should I want the language to support associative arrays directly? > Do you > really think that I, like you, willfully misinterpret what is being > said to support a different point of view without trying either to make > a point or learn something from somebody else? Look, kid, I'm not misinterpreting what's being said here. People are saying that languages with built-in support for dynamic typing are somehow better than languages without such support. I'm reacting to that statement. On the occasions when I want dynamically typed variables, I can use them without trouble in C. Sure, this requires a void pointer type. SO WHAT? As I pointed out when I entered this discussion, the language does need *some* type that can represent *all* values if it's going to support dynamic typing. C has such a type, and adequate macros, and that's enough for me to get work done. *You* are misinterpreting my comments as something like ``Dynamic typing is useless. Anyone who wants to use dynamic typing is a fool.'' I'm just saying that it's pointless to have the compiler support dynamic typing, as it's just as easy to do in a library. ---Dan
peter@ficc.ferranti.com (Peter da Silva) (03/28/91)
In article <28436@dime.cs.umass.edu> yodaiken@chelm.cs.umass.edu (victor yodaiken) writes: > We're going to write a real-time operating system in a language with > major run-time components? OK, a message queue for a non-real-time-but-pretty-fast operating system. "Real time" slipped out without thinking, because non-real-time systems don't seem to use things like message queues as basic primitives. Pity, because they are damned useful. -- Peter da Silva. `-_-' peter@ferranti.com +1 713 274 5180. 'U` "Have you hugged your wolf today?"
adam@visix.com (03/29/91)
In article <24673:Mar2802:15:1091@kramden.acf.nyu.edu>, brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes: |> In article <1991Mar27.164605.24547@visix.com> adam@visix.com writes: |> > Let me get this straight. To construct a heterogeneous list of M |> > items each guaranteeing N operations, I need to waste space for M*N |> > function pointers? |> |> No, because you can just have a single ``class'' structure with the N |> function pointers, and keep just one pointer per ``object.'' But this |> extra layer is pointless for small problems, and I didn't bother putting |> it in. Okay, all objects of the same class can share a function table. But I would still have to make each "list of {fooable, barable}" and "list of {fooable, bazable}" use a new and different class, each with their own function table and constructor. And so a {fooable, barable, bazable} object is still classified twice, which is a waste. People want to use dynamic typing when they expect large numbers of different lists with different (and arbitrary) commonalities. |> > And I need to write an explicit constructor for every kind of list? |> |> That depends on what you mean by ``explicit.'' If you have several types |> each supporting some sort of redraw operation, and you want to set up a |> list of redrawable things, then you do have to say the correspondence |> between ``redrawable'' and the redrawing for each type. Other posters |> have mentioned how in object-oriented languages you can make the |> redrawing class either a superclass or a subclass of all the classes in |> question; either way provides the necessary correspondence, but I |> wouldn't say it's explicit. |> |> ---Dan As I see it, your form of dynamic typing requires a class heirarchy, with multiple inheritance yet. I think, for instance, the dynamic typing in GNU Emacs is better. Adam
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)
new@ee.udel.edu (Darren New) (03/29/91)
In article <27MAR91.22130574@uc780.umd.edu> cs450a03@uc780.umd.edu writes: >Did I say that? hmm... >If I have a bitmap, that's a homogenous collection, and I sure don't >carry type information along with each bit. Well, comparing it to the example of buttons and subviews and scrollbars in a list, yes you said that. Of course, there will be homogenous lists where you don't carry the types along. But that isn't what we were talking about. We were talking about seemingly-hetro lists which you said were actually homogeneous. >On the other hand, if for some reason I wanted a collection of bitmaps >and strings, I'd have a list of pointers to data descriptors. One >pointer (and descriptor) for each element of the list. Those data >descriptors would carry "type" [and size], etc. But I deal with a >homogenous collection: a list of pointers. And I deal with a hetrogeneous list of both strings and bitmaps. I look at how I use the data, and you look at how you implement hetrogeneous collections. I don't want to deal with pointers to descriptors; I want to deal with bitmaps and strings. How that gets implemented is secondary to the problem that needs strings and bitmaps to solve. Similarly, I want to deal with symbolic names for functions and variables and let the linker deal with resolving it instead of working with stack-frame offsets; where a variable is in the stack is not part of the solution to my problem. When I have a floating point number, I don't want to deal with a 3tuple of <sign, exponent, mantissa> and recode all the floating point operations in terms of this. I want to deal with a float directly. Of course, assembly language (or hardware) will have to deal with it as a 3tuple, but I don't want to. The closer a language can come to providing the data types required by my problem, the easier it will be to solve that problem. If the language lets me deal with hetrogeneous collections directly by allowing the type of a variable to change dynamically over time, then I have an easier time dealing with bitmaps and strings. If I have to implement bitmaps and strings as pointers to data descriptors carrying a type tag and then within my routines check the type tag, I have more work to do without any more solved problems. I also don't want to have to tell bitmaps and strings that they may both be put in a list together. The fact that a bitmap might appear in the same list as a string is completely irrelevant to the functionality of a bitmap. Requiring me to do so would be like requiring me to list all of the variables that may hold a bitmap *in*the*description*of*bitmaps* instead of in the descriptions of the variables; clearly, this reduces reusability. >Personally, if I have bitmaps and strings, I'd prefer to refer to them >by separate names (that is, store them in separate variables). And as >a general rule, this runs faster (than using indirection). I too. However, sometimes (like with a window containing a graphic view and a textual view and buttons and sliders and ...) you need to put things of different types that behave similarly in some way, together. I sometimes need to store them in a hetrogeneous collection. Just like sometimes I store both source and executables in the same directory. I don't want to have to tell 'ls' in advance what the possible types of files are in each directory. I don't deny that static typing is useful; I don't think anybody here has. I don't deny that dynamic typing can be implemented on top of static typing and typecasts/unions; objective-C does exactly this. I don't deny that hetrogeneous lists can be implemented by homogeneous lists of pointers to tagged data; I don't know of any dynamically-typed languages where this isn't how it's done. I deny that dynamic typing is useless: all dynamically-typed languages I've seen come with a large reusable class languages. I deny that hetrogeneous collections are never needed; I've provided many lists of hetrogeneous collections that people use every day. I deny that the best way to think about dynamic typing is by worrying about how one could implement it in C. >Raul Rockwell -- Darren -- --- Darren New --- Grad Student --- CIS --- Univ. of Delaware --- ----- Network Protocols, Graphics, Programming Languages, FDTs ----- +=+=+ My time is very valuable, but unfortunately only to me +=+=+ + When you drive screws with a hammer, screwdrivers are unrecognisable +
new@ee.udel.edu (Darren New) (03/29/91)
In article <25655:Mar2803:50:2491@kramden.acf.nyu.edu> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes: >Dynamic typing isn't a semantic feature. It's a state of mind. Exactly. Modularization is also a state of mind. So are local variables. So is recursion. So is separate compilation. So are libraries, for that matter. None of these can't be done in raw machine code (with suitable preprocessors ;-). However, when the language I use supports my "state of mind" then I find I can be much more productive. When I implement dynamic typing myself, I have to worry about type mismatches that aren't caught by the compiler. If I let the compiler/interpreter handle dynamic typing, it catches the errors for me. Just like when I implement modularization or local variables myself, I sometimes make mistakes which the compiler can't catch because I can't tell it that a particular variable is local. I would much rather adjust once the computer to match my state of mind than to adjust my state of mind to match the computer every time they disagree. >If you >want to use the pair (type,value) as a value with certain constraints, >you can, whether the language supports it or not. It takes at most a >good preprocessor to make this just as convenient as in any >``dynamically typed'' language. You mean a good preprocessor like Lisp, or Objective-C? Wow, heavy. I didn't realize I can do the Objective-C transformations by hand and get the same results :-O. Of course, without a good preprocessor (which most of the rest of us have been calling "a dynamicly-typed language compiler") it is damn inconvenient. >*You* are misinterpreting my comments as something like ``Dynamic typing >is useless. Anyone who wants to use dynamic typing is a fool.'' I'm just >saying that it's pointless to have the compiler support dynamic typing, >as it's just as easy to do in a library. I'm not interpreting your comments that way. I'm interpreting your stance as saying that dynamic typing implemented by the user on top of static typing is just as useful and convenient as dynamic typing in a language. I disagree that dynamic typing innards being visible to the user is a "good thing" any more than local variable management during recursion being managed by the user is a "good thing." I don't care what semantics the language supports. As you and I have both pointed out, all programs can be reduced to huge finite state machines. The question is not the semantics of the language, but rather how well the syntax I use to describe this huge fsm matches how I think about the problem. > >---Dan -- Darren -- --- Darren New --- Grad Student --- CIS --- Univ. of Delaware --- ----- Network Protocols, Graphics, Programming Languages, FDTs ----- +=+=+ My time is very valuable, but unfortunately only to me +=+=+ + When you drive screws with a hammer, screwdrivers are unrecognisable +
cs450a03@uc780.umd.edu (03/29/91)
Darren New >> Me > >>If I have a bitmap, that's a homogenous collection, and I sure don't >>carry type information along with each bit. > >Well, comparing it to the example of buttons and subviews and >scrollbars in a list, yes you said that. Of course, there will be >homogenous lists where you don't carry the types along. But that isn't >what we were talking about. We were talking about seemingly-hetro lists >which you said were actually homogeneous. Well, I guess I should pay attention, and find out what I was really saying... 8-) Or make yet another attempt to have what I think I'm saying the same thing as other people understand me to be saying. Maybe if I clarified my terms? A list which was made by sticking a string of bits onto the end of a string of characters would be hetrogenous. Each bit and each character would need a type tag. The list's type in this case is not "string of bits" and not "character string", but "hetrogenous". The language I use is dynamically typed. Each structure has size and type information associated with it. If I made a list which was made by catenating a list of structures onto a list of bits, then each element would have to have a type tag: either "structure" or "bit". You might call that silly. I call that hetrogenous. I was very careful, each time I called a list of structures homogenous, to point out that this was my point of view, and not an attempt to classify all problems for all people. I hope my statements are now understandable. Raul Rockwell
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
marcs@slc.com (Marc San Soucie) (03/30/91)
Joseph Pallas writes: > 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. This is pretty left-field thinking. A collection is an entity, not an activity. The collection exists regardless of what you do or whether you do anything with it. So argue the definition of the entity, not the operations that are to be performed on it or its members. Yes, in Smalltalk there is a base class from which everything inherits, so yes, you can claim that in Smalltalk there cannot ever be a truly heterogeneous collection. So in C++ every object is a bunch of bytes. Big deal. How about a more accurate but possibly meaningless definition of a heterogeneous collection: A collection on the types of whose members there are no explicit constraints. From which derives the possibly equally meaningless theorem: It is easier to construct such a collection in a Smalltalk-like language than in a C++-like language. Which leads me to wonder: Can this discussion lead to any meaningful insights? Marc San Soucie Servio Corporation Beaverton, Oregon marcs@slc.com
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
sommar@enea.se (Erland Sommarskog) (04/01/91)
Also sprach Darren New (new@ee.udel.edu): >Hmm... a homogenous collection that when you take it apart yields things >with different types. Somehow, I think we are confused over terminology. >Either that, or you are reaching for straws :-) The whole point of >a hetrogeneous collection is that sometimes you treat everything >the same and sometimes you treat each thing differently. I think no one questions the use of heterogeneous collections. But how common are the cases when the collections is heterogeneous that they don't even have a common inheritance structure which can't be described in a statically typed language. Static typing does not preclude dynamic binding. -- 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! ----------------------
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?"
chip@tct.com (Chip Salzenberg) (04/02/91)
According to carroll@cis.udel.edu (Mark Carroll): >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:". I cannot cast my argument in this form, because that's not the point of this thread. The original question was, "Why do you _need_ heterogenous collection?" I have enjoyed debunking some of the replies. However, the heterogenous/homogenous collection issue is really a side issue for me. I like static typing for its measure of safety from message-not-recognized errors. If that forces me to use the C++ preprocessor to make collection classes, I can live with that. >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. Agreed, as far as it goes. But I'm not willing to sacrifice type safety for that notational convenience. -- Chip Salzenberg at Teltronics/TCT <chip@tct.com>, <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
pallas@eng.sun.com (Joseph Pallas) (04/02/91)
In <b32pvj.em4@wang.com> marcs@slc.com (Marc San Soucie) writes of my definition of heterogeneous collections: >This is pretty left-field thinking. A collection is an entity, not an >activity. The collection exists regardless of what you do or whether >you do anything with it. So argue the definition of the entity, not >the operations that are to be performed on it or its members. Now, I have to admit that, at first glance, it may seem odd that the definition I offered has this dependency. But I think I can explain why it does. We all seem to agree that "heterogeneous" has something to do with the types of objects. The problem arises from the very nature of OOP in that it emphasizes inclusion polymorphism (usually in the form of subtypes). The consequence of this is that it is not possible in general to speak of "the type" of some object. Objects have many types, as the types are organized hierarchically in a containment relationship. Now, we can (as Marc pointed out) simply stop there and say that in Smalltalk, for example, there are no heterogeneous collections because there is a type of which all objects are members. But that clearly doesn't capture the notion we have in mind when we talk about heterogeneous collections. So, given a collection of objects and a desire to talk about the types of those objects, how do we decide which types to use? I claim that, in a dynamically typed language like Smalltalk, the only types we can sensibly use for this kind of discrimination are the types implied by the operations we actually perform. >How about a more accurate but possibly meaningless definition of a >heterogeneous collection: > A collection on the types of whose members there are no explicit > constraints. This isn't very interesting, since this definition makes all Smalltalk collections heterogeneous. It doesn't capture the common notion that collections are either homogeneous or heterogeneous. As for the question of whether this discussion is leading anywhere, I suspect not. joe
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!"
kers@hplb.hpl.hp.com (Chris Dollin) (04/02/91)
Dan Bernstein says: Dynamic typing isn't a semantic feature. It's a state of mind. If you want to use the pair (type,value) as a value with certain constraints, you can, whether the language supports it or not. It takes at most a good preprocessor to make this just as convenient as in any ``dynamically typed'' language. I call this ``designing a new language and compiling into [in this case] C''. C doesn't have [*1] dynamic typing, and the new language does. Look, kid, I'm not misinterpreting what's being said here. People are saying that languages with built-in support for dynamic typing are somehow better than languages without such support. I'm reacting to that statement. On the occasions when I want dynamically typed variables, I can use them without trouble in C. The last sentence merely highlights Dan's competence as a programmer; not all of us are so lucky. -- Regards, Kers. | "You're better off not dreaming of the things to come; Caravan: | Dreams are always ending far too soon."
new@ee.udel.edu (Darren New) (04/03/91)
In article <28MAR91.21053267@uc780.umd.edu> cs450a03@uc780.umd.edu writes: >Darren New >> >Me > >>>If I have a bitmap, that's a homogenous collection, and I sure don't >>>carry type information along with each bit. >> >>Well, comparing it to the example of buttons and subviews and >>scrollbars in a list, yes you said that. Of course, there will be >>homogenous lists where you don't carry the types along. But that isn't >>what we were talking about. We were talking about seemingly-hetro lists >>which you said were actually homogeneous. > >Well, I guess I should pay attention, and find out what I was really >saying... 8-) Or make yet another attempt to have what I think I'm >saying the same thing as other people understand me to be saying. Sorry. Looking back on it, I realize that my above-quoted material sounds quite rude. I didn't mean it that way, and I apologise. I think sometimes these language/flame wars go off because people take statements out of context and then change the context, and I suspect it is because of the passage of time between comments, and not on purpose. My interpretation of the conversation went as follows: I said "a window is hetrogeneous and thus you need dynamic typing" and you said "I treat that as a collection of pointers to tagged structures" and I said "I would implement it that way but I would treat it as a collection of buttons and texts and ..." and you said "but it would be silly to treat a bitmap as an array of pointers to tagged structures" and I said the above. (Or at least, that's how I interpreted it.) Anyway, my point is that I'm sorry if I sounded like you were weasling or if I was rude. I didn't mean it that way. I just meant that the point you were making was not the point I was arguing and that I agree with you in the case you were arguing. It sounded like you were changing context, and I merely wanted to say "in the context which you first made that statement, it seemed to me that you did not mean what you meant by the same context in the current statement." How's that? :-) >Maybe if I clarified my terms? > >A list which was made by sticking a string of bits onto the end of a >string of characters would be hetrogenous. Each bit and each >character would need a type tag. The list's type in this case is not >"string of bits" and not "character string", but "hetrogenous". Sounds like ASN.1: SEQUENCE OF { CHOICE {alpha OCTETSTRING, beta BITSTRING}} (modulo memory faults) Anyway, so why would you not call a list inside of a window structure that tells the window what buttons, scrollers, and other subviews it contains, a hetrogeneous list rather than a list of pointers to tagged structures? I see little difference between this list and a list of "string of bits or characters" except for the size of the structures. >The language I use is dynamically typed. Each structure has size and >type information associated with it. If I made a list which was made >by catenating a list of structures onto a list of bits, then each >element would have to have a type tag: either "structure" or "bit". >You might call that silly. I call that hetrogenous. I call that hetrogeneous also. Are you saying that because the elements of the window list are not stored inline then the list is not hetrogeneous? Or is it because the type tags in the window example are stored with the elements of the list rather than actually in the list? What about the case where the list, the types, and the bits are all physically stored disjointly? Or is it that in one case, you would mentally envision it in one way and in the other case you would mentally envision it the other way, regardless of implementation? In that case, I don't think there is much more to say except that a language that allows both visualizations is probably better than a language that allows only one such viewpoint. >I was very careful, each time I called a list of structures >homogenous, to point out that this was my point of view, and not an >attempt to classify all problems for all people. Again, I apologise if I sounded rude or nasty. That was not the intent. I also have trouble when people misinterpret what I was unable to state clearly enough. I believe at least one of my followups said something like "you prefer to treat it as a list of pointers and I prefer to treat it as a list of stings and bitmaps". I realize that it is *all* point-of-view because at the bottom level, it's all bits. I was merely trying to keep the discussion focused on hetrogeneous lists which could be interpreted as homogenous, and not on clearly-homogenous lists. >I hope my statements are now understandable. >Raul Rockwell Hmmm... Tell the truth, it didn't really clarify much, unless you mean that a list of two different types of structures will always be homogenous because you always store "pointers to tagged structures" but an intermixed list of bits and characters is hetro because they are physically stored ajacent to each other. -- Darren -- --- Darren New --- Grad Student --- CIS --- Univ. of Delaware --- ----- Network Protocols, Graphics, Programming Languages, FDTs ----- +=+=+ My time is very valuable, but unfortunately only to me +=+=+ + When you drive screws with a hammer, screwdrivers are unrecognisable +
cs450a03@uc780.umd.edu (04/03/91)
Darren New writes: [big long apology] ((thanks, wasn't expecting that)) [recap of discussion thread] ((looks more-or-less correct)) >Anyway, so why would you not call a list inside of a window structure that >tells the window what buttons, scrollers, and other subviews it contains, >a hetrogeneous list rather than a list of pointers to tagged structures? >I see little difference between this list and a list of "string of bits >or characters" except for the size of the structures. > >Are you saying that because the elements of the window list are not >stored inline then the list is not hetrogeneous? Or is it because the >type tags in the window example are stored with the elements of the >list rather than actually in the list? What about the case where the >list, the types, and the bits are all physically stored disjointly? >Or is it that in one case, you would mentally envision it in one way >and in the other case you would mentally envision it the other way, >regardless of implementation? Hmm... (Why) Because I can operate on the list without checking the types. This occurs because I'm not writing the windowing code, you are :-) I suppose, also, it is because none of the information is stored inline is the reason I'd call the list homogenous. That is, perhaps, an arbitrary distinction. I don't put a whole lot of emphasis on the type-tags which the language uses to manage storage. I have to be aware of them because they have an impact on efficiency and speed, but... hmm... I look at "type" as meaning "x is in the domain of function f". So I might, for instance, consider even numbers to be a type, or maybe sets composed of unique elements from the ranges 1 .. 7, 1000...9999, and/or 100000...100999. Now if I had three functions, one of which took as arguments numbers in the range 1..7, another 1000...9999, etc., then maybe I'd consider those sequences of numbers to be hetrogenous lists. The point being that I had to write three separate functions to handle them, and I have the overhead of a conditional in front of each function application. Maybe that's an artificial distinction. If I wanted to make sure that these numbers weren't accidentally treated as arithmetic objects, I'd have to encapsulate them anyways (put them in structures and work with pointers to these structures). So you could say I'm being backwards... It boils down to: I like to think of "pointers" as a single type. It is only when I examine the data that they are pointing to that I like to think of the data as being typed. This is influenced by the primitives I normally work with. >>I hope my statements are now understandable. >>Raul Rockwell > >Hmmm... Tell the truth, it didn't really clarify much, unless you mean >that a list of two different types of structures will always be >homogenous because you always store "pointers to tagged structures" but >an intermixed list of bits and characters is hetro because they are >physically stored ajacent to each other. Ahh... It is not that I consider the structures to have the same type. It is that I consider the pointers to have the same type. All of my primitives that work on pointers (except the ones that extract information from the encapsulated structure) will work on any list of pointers. This is just an off-the-cuff sort of classification, I admit. If there is anything left to say on this topic, it is probably on the distinction between what a function can meaningfully operate on vs. what information can be meaningfully stored. Raul Rockwell
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)