eberard@ajpo.sei.cmu.edu (Edward Berard) (01/07/90)
As promised, this message includes some of the discussion which took place last August and September on comp.lang.smalltalk. Specifically, it is the discussion of the possible meanings of "types" in Smalltalk. I am posting this message for two reasons: 1. To let people know that there is a variety of views on the meaning of types in an object-oriented programming language, and 2. To stimulate some discussion on the topic of types and object-oriented thinking. -- Ed Berard (301) 353-9652 ----------------------------------------- From: alan@ux.cs.man.ac.uk (Alan Wills) Newsgroups: comp.lang.smalltalk Subject: Re: Smalltalk types Date: 8 Sep 89 10:42:27 GMT Organization: Dept. of Computer Science, University of Manchester, UK. Robert Dew's list of sets of Smalltalk classes which understand the same set of messages is worth contrasting with the notion of a type as being the set of objects which understand a particular set of messages. For example, in Smalltalk, if we look at the set of all objects which understand the messages "<", "=<", ">", ">=", "=" : we get all instances of Magnitude (and its subclasses) and all instances of String (and its subclasses). Even more interesting is that, although the implementations of these functions are different in the two inheritance subtrees, this correspondence isn't just a mere overloading of names: the relations obey the same rules about how they're interrelated. For example: if I know and also then I can infer x < y (y < x) not x =< y (x = y) not x < y x > y y < x x < y y < z x < z etc I guess this helps to illustrate that this notion of type --- equivalence sets wrt parts of behaviours --- is useful when into the nitty-gritty of reasoning about programs to check they're going to do the right things. Alan Wills Manchester University, UK +44-61-273 7121 x5699 -------------------------------------------------------- From: wilson@carcoar.Stanford.EDU (Paul Wilson) Newsgroups: comp.lang.smalltalk Subject: types as roles [was Re: Smalltalk types] Keywords: types, first class types, protocols, type safety, static typing, roles Date: 12 Sep 89 18:59:09 GMT Reply-To: wilson@carcoar.Stanford.EDU (Paul Wilson) Organization: Stanford University I got several responses to the effect that my types-as-roles model is very intuitive, but pointing out what at first appears to be a problem with multiple inheritance. I don't think it's really a problem, and in fact is a major strength if you believe in strong typing. [ to recap a little: I said that types should be *roles* that objects can play, which is a lot like sets of messages they respond to. It's stronger than that, though, because it's an explicit declaration that the object understands the messages in a particular way. For example, what makes an object a number is not just that it understands messages like ">", but that it promises to respond to them in a way that makes sense as a number. This avoids accidental coincidences of naming, and in software engineering terms helps establish responsibility for whether something can be used in a particular context. I made the example of an object that is a window and is a queue as well, and can respond to the "size" message as either one; if you have a pointer to it as a window, it responds as a window, returning a point giving its screen dimensions. If you have a pointer to it as a queue, it returns an integer telling how many elements are in the queue. ] The problem comes up that you may have a window-queue object with a message name collision like this, and you may have a pointer to it as a window-queue, rather than as either a window or a queue. What happens then? I submit that you should have to explicitly disambiguate the context. This is not as gross and restrictive as it sounds, and in fact it's not restrictive at all. Remember, roles are *external* interfaces, and may cross-classify with inheritance for code reuse. If you have a pointer to something that can behave as a window or behave as a queue, it's no violation of information hiding to tell it what you mean. If you take the conversational message-passing metaphor seriously, this is exactly how things ought to be. If I've established more than one conversational context with somebody, ambiguities must be resolved. If I'm at a CS conference but I'm talking to somebody I've recently talked to about windsurfing, a question like "what do you do?" has to be disambiguated: "what do you do? -- as a hacker, I mean --" vs. "what do you do? Wavesailing, slalom?" This is not an invasion of my privacy because both contexts have already been established. Presenting myself as a CS guy and a boardhead does *not* imply, for example, that I'll answer questions about my religion or political views or whether my family is thoroughly inbred white trash: disambiguating *roles* doesn't imply knowing anything about the *class* of a reciever that it hasn't already told you. Oddly, I just came across something like this in a thoroughly *dynamically* typed language -- Ungar & Smith's "Self" language, which has no classes at all, just prototypes. In Self, multiple inheritance lookups are guided by the "sender path constraint," which sort of like the kind of disambiguation I'm talking about. It only works with messages an object sends to itself, though, because other objects can't tell anything about the type of the receiver. Basically, if an window-queue object sends itself a "size" message while executing a message inherited from "window," it responds with its window version. If it's executing a method inherited from "queue" when it sends itself a "size" message, it likewise assumes it's considering itself a queue at that point, and responds with the queue size. Actually, though, Self isn't making a distinction here between interface and implementation -- it assumes that they correspond. In a virtuous strongly-typed language (with all the Truth, Beauty, and Righteousness that does or doesn't imply), the roles would be explicit and checked. Since the size method of a queue is probably not an implementation of the same interface as the one for a window, no conflict would occur. If it *is* the programmer should know about it and have to say what he/she means. In general, I see Ungar's point of view that strong typing is a hassle and a pain in the ass. But I'm not sure you couldn't get rid of most of the hassle by the use of defaults and a little automation. For example, when you implement a subclass, it would be, by default, a subtype of the type(s) of its parent(s). And if you write code that assigns a particular value to an untyped variable, the type is inferred, etc. In general, pretty safe guesses would be made and the programmer's attention brought to the ambiguities. The latter might be deferred until runtime errors, or a tightening-up phase after a prototyping phase. You might have an optionally-typed language with automated support for going back in and strongly-typing things as needed. (Either to satisfy your anal-retentive supervisor :-) or if you feel that maybe your program's doing the wrong thing because you have undetected role mismatches.) It seems to me that programmers generally have a very good idea of what roles are appropriate at what point in a piece of code, and the trouble is not in any serious brainwork or overcommitment that the programmer must do, but in some low-level explication that might be largely automated, getting the best of both worlds. (Note that the programmer could always use the degenerate role "object" for things that must truly be dynamically typed. But for things in which the intent of the code obviously restricts the plausible roles anyway, like generic numeric code, specifying the role "number" is not restrictive. And if in some cases the programmer overcommits, then there should be automated support for generalizing, like going back and substituting a more general role and pointing out any mismatches or ambiguities it causes. This might actully be *more* flexible than dynamic typing, because some of the "freedom" of dynamic typing creates more work by making it harder to find hidden assumptions and dependencies. If you overcommited when you wrote the code, you might as well have done it explicitly, so a tool can help you fix it.) BTW, David Keppel has pointed out to me that Eiffel has a renaming facility, but it's really not doing the kind of disambiguation that I mean. It's for disambiguating name collisions *across* contexts, to establish a single non-role-dependent interface. This seems wrong and against the object-oriented grain to me. If an object is a queue and a window, it should be able to function both as a queue and as a window, without breaking existing code. The window management software should not have to be changed, and neither should software that uses queues. It should not matter to either one what some object does in other contexts, just as it is irrelevant that a word in English means something else in French. The introduction of bilingual entities should not require changes to either language, unless there's some fortuitous advantage to it. Paul R. Wilson Software Systems Laboratory lab ph.: (312) 996-9216 U. of Illin. at C. EECS Dept. (M/C 154) wilson@carcoar.stanford.edu Box 4348 Chicago,IL 60680 ---------------------------------------------------------- From: johnson@p.cs.uiuc.edu Newsgroups: comp.lang.smalltalk Subject: Re: types with identity > protocols, no Date: 12 Sep 89 03:41:00 GMT >What happens when an object is both a queue and a window (say, >because it's a queue that can represent its contents to the >user)? >It seems to me that the "right," intuitive way to do this, >in a strongly typed system, is to resolve this by the >role the object is playing. If the window system has >a pointer to it *as a window*, it should respond as >a window. And if a simulation object has a pointer to >it *as a queue*, it should respond as a queue. >Does anybody do types this way? I don't do types that way, but I do OBJECTS that way. In other words, I wouldn't define one object, but two separate objects that make up a multifaceted object. If you want a window then you get a window object (probably the "window" message retrieves it) and if you want a queue then you get a queue object. The queue and the window know about each other and can be thought of as different facets of a complex object, but are implemented as separate objects. This seems simple and easy both to implement and to think about. Most o-o windowing systems work this way. Ralph Johnson ------------------------------------------------------------- From: johnson@p.cs.uiuc.edu Newsgroups: comp.lang.smalltalk Subject: Typed Smalltalk at OOPSLA Date: 14 Sep 89 03:56:00 GMT The goal of the Typed Smalltalk project is to build an optimizing compiler for Smalltalk. We are not trying to invent a new programming language, and we consider the Smalltalk programming environment just as important as the language, so we expect the browser and debugger to work pretty much like in Smalltalk-80. The major difference that the user sees from Smalltalk-80 is that methods have types; the compiler needs this information for optimization. Fortunately, we have had great success with type inference, so the type system should not destroy the advantages of Smalltalk for rapid-prototyping. The compiler produces "good" code; it is very fast when it works. As is typical of university projects, the compiler is still unreliable, but there are good reasons to hope that it will be reliable soon. The compiler currently runs inside Smalltalk-80 and produces machine language that can be executed from user primitives. The result is esentially the ability to write user primitives in Smalltalk. Our long term goal is to compile the entire image and ignore the interpreter, to make it easy to integrate Smalltalk with other languages, and to make it easy to run Smalltalk programs outside the image, but we still have a bit of work to do before we achieve these goals. I will be at OOPSLA and am interested in talking with people about Typed Smalltalk. Monday is completely open. Since many people are coming later, a "birds of a feather" session during an evening might be a better idea. In any case, you can come and look for an announcement or you can try to get in touch with me before-hand. Ralph Johnson -- University of Illinois at Urbana-Champaign johnson@cs.uiuc.edu (217) 244-0093 ----------------------------------------------------- From: scc@cl.cam.ac.uk (Stephen Crawley) Newsgroups: comp.lang.smalltalk Subject: Re: types as roles Keywords: types, first class types, protocols, type safety, static typing, roles Date: 16 Sep 89 01:04:38 GMT Organization: U of Cambridge Comp Lab, UK Paul Wilson's types-as-roles model bears more than a passing resemblance to the type system of the distributed persistent object system that I am working on. Objects or *entities* are characterised 3 components; a *class* which defines an abstract interface, an *implementation* that provides the routines etc that implement the operations defined by the class, and the *representation* which is understood by the implementation routines. (The separation of interface from code in my type system allows a class to have a number of implementations.) Entities exist in two states. *Passive* entities have all their state in stable store and are signified by an *identifier* or *id*. *Active* entities may have some of their state in local (non-stable) memory and are signified by a *handle* whose type is a class. A handle for an entity is obtained by applying a classes *open* operation, passing the entity id as an argument. When a client wants to use an entity (i.e. to invoke operations), it must have a handle for the entity. The class of the handle determines the operations the client may invoke. This class is called the client's *perspective class*. On the other hand the entity's *actual class* is the class that corresponds to the entity's implementation. Normally, the client's perspective class and the entity's actual class are the same. If they are not, the client will invoke operations defined by the perspective class, and these operations will be *projected* onto operations provided by the actual class. The *projection* is set up as the entity is opened by a piece of code called a *resolution function*. Projections may be simple (i.e. a renaming or a restriction) or arbitrarily complex depending on the resolution function. It was originally intended that the mechanism be used for restricting access to an entity's operations, and for making evolutionary changes in a persistent type system. In practice, projection turns out to be far more powerful mechanism. We have used it to perform a variety of tasks ranging from displaying objects and synthesising interactive tools through to selecting versions in a version control system. The current version of the entity type system does not support static inheritance (i.e. classes don't have subclasses). It will do, but I haven't got around to it. I'm currently concentrating more on garbage collection and distributed programming aspects. For further information, check out the following paper: "Flexibility in a Persistent Object-based Type System" H.J. Barman and S.C. Crawley in Proceedings of the Workshop on Persistent Object Systems, Appin Scotland, August 1987 Persistent Programming Research Report #44 University of Glasgow, Dept of C.S. The address for U of G, CS is University of Glasgow, Dept of Computing Science, Lilybank Gardens, Glasgow G12 8QQ Great Britain. Papers on the latest incarnation of the Entity System are in the pipeline. -- Steve