Xref: utzoo comp.lang.misc:7093 comp.object:2902 comp.lang.eiffel:1466 Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!usc!snorkelwacker.mit.edu!stanford.edu!leland.Stanford.EDU!elaine13.Stanford.EDU!hoelzle From: hoelzle@elaine13.Stanford.EDU (urs hoelzle) Newsgroups: comp.lang.misc,comp.object,comp.lang.eiffel Subject: Re: CHALLENGE: typing and reusability (was: Re: blip) Message-ID: <1991Mar27.040253.17343@leland.Stanford.EDU> Date: 27 Mar 91 04:02:53 GMT References: <1991Mar22.210725.29448@neon.Stanford.EDU> <1991Mar26.005805.1914@kodak.kodak.com> Sender: news@leland.Stanford.EDU (Mr News) Organization: Stanford University - AIR Lines: 93 In <1991Mar26.005805.1914@kodak.kodak.com> cok@islsun.Kodak.COM (David Cok) writes: > However, I do prefer static typing where it works and am interested in ^^^^^^^^^^^^^^ > discovering how to make it work better. In any case Hoelzle's programming Sure... I guess we just disagree what "works" means :-) But now to the technical points: your code example is nice, but (as you mention yourself) it doesn't solve the problem because of a typing snag, and that was exactly my point. Furthermore, it has two other problems: 1) It assumes that you have source code access to A and B. You write that > If a programmer did not > have source code access to A and B, one could still create classes MyA (MyB) > which inherited from both A (B) and FooDisplayable. The programmer would have > to use MyA and MyB everywhere instead of A and B (at least where things might > be put on the list) and A and B would have had to declare display and foo > virtual. This doesn't work as soon as you also have Lists of A (or other classes parametrized by A) in existing modules, because List[MyA] is not a subtype of List[A]. This is a result of the contravariance rule (see e.g. "A Proposal for Making Eiffel Type-Safe" by W. Cook, ECOOP'89; "Introduction to Trellis/Owl", OOPSLA'86 [I might have gotten the years slightly wrong]). The problem isn't really with contravariance, but with the way it is applied: type-checkers don't only check the validity of your program, but also the validity of *all possible variations*: someProc(List[FooDisplayable]) is illegal because someProc *could* use List::insert to insert a Fooable instead of a FooDisplayable, which would violate the type assertion for the list. HOWEVER, the existing version of someProc *does not* use any "dangerous" List operation such as insert() and is perfectly safe. But today's type checkers are unable to recognize this because they still live in a world of batch-style compilation and are prohibited from looking at the *actual* use of objects. Therefore, they have to use worst-case assumptions about the *potential* uses and unnecessarily restrict the programmer. [For Eiffel programmers: before you agree with me, let me say that what Eiffel does is *not* what I propose here. Eiffel's "solution" to the problem is, IMHO, totally wrong. :-)] > As the original poster pointed out, this will not give any runtime message > errors in a dynamically typed system. However, I'd maintain that it is a bad > use of dynamic typing. The only thing that prevents errors is the semantic > knowledge that someProc only does foo and not anything else that Footype allows. > If someProc were modified later by someone who did not know that it was applied > to a FooDisplayable list, errors would result. Sure, type errors would result in a statically-typed language, and run-time errors in a dynamically-typed language. There's no problem with that: if your change transforms a type-correct (in my sense, not C++'s) program into an unsafe program, you get an error. 2) You need a new type of iterator for every type of message you want to send to the list. This is actually orthogonal to the issue of dynamic vs. static typing since it is about closures. In Self/Smalltalk, you define one iterator which takes a block. The block is the called by the iterator, once for every element, with the element as an argument. Thus you can do list do: [ | :elem | elem foo ] "original example" list do: [ | :elem | elem bas: 15] or (assuming that prev is a local in the caller of do:) prev: nil. list do: [ | :elem | elem baz: prev. prev: elem ] You could introduce closures to C++ et al, but it's interesting to observe that no statically-typed language has them, while many dynamically-typed languages do. You could also simulate this in C++ with function pointers, but you have to "invent" a procedure for every (trivial) block... reminds me of COBOL where you were forced to write your loop bodys as separate sections ;-) -Urs PS: > >>>>> Compiling takes too long. I want runtime syntax checking! <<<<< Hey, you can have this in Self - almost every string of characters with balanced parentheses is a legal Self expression. :-)