For context, see LanguageTypeErrors.
A problem that can be observed in reality is the problem of parallel hierarchies: a vehicle and the corresponding vehicle driver, a car and a car driver, a bus and a bus driver etc. Modelling these classes and the interaction of these classes without covariance is very difficult.
This is a good motivation for covariant arguments, but covariant arguments are no solution. If you "model" this situation (modelling something without keeping in mind what the program should do is imho useless) you say "a bus is a vehicle, a car is a vehicle, a bus driver is a driver, a car driver is a driver" and then make the mistake to also say "any vehicle is driven by any driver", which leads to a car driver driving a bus into a tree. When the police pulls him out of the wrecked bus, he claims "I'm allowed to drive the thing! It's a vehicle, and I'm a driver, dammit!"
The problem is that in reality "Any vehicle is driven by some driver". This fact however is useless when programming: saying that some drivers can drive a given vehicle doesn't tell you if any particular driver can do it, so you could never call the method. Utterly useless when programming. To model this correctly, you need an ExistentialType?. If Eiffel was indeed a language for modelling reality (which is a stupid concept), and BertrandMeyer didn't recognize the need for existential types, that would have been his second major blunder. But I think he's too sane to even attempt such nonsense as an object oriented model of the world.
The paper in ContraVsCoVariance does not describe the Eiffel implementation; instead, it describes a form of MultipleDispatch. (Incidentally, the simplicity of the paper's mechanism only holds in the presence of SingleInheritance; having MultipleInheritance complicates things quite a bit). Covariance in the form of MultipleDispatch is no problem. All of the above discussion implicitly assumes a SingleDispatch OO language. Outside CommonLispObjectSystem and its derivatives (DylanLanguage, CecilLanguage), virtually all production OO languages are SingleDispatch, including CeePlusPlus, ObjectiveCee, JavaLanguage, CsharpLanguage, SmalltalkLanguage, PythonLanguage, RubyLanguage, and, yes, EiffelLanguage.
what's the issue with "catcalls"?
A catcall (cat stands for changing availability of type) is a method call against an object X.M(...), such that the entity X is statically resolved to have the type ClassX and ClassX has a derived class ClassY that overrides M(...) in a manner incompatible with subtyping, e.g., removing it or declaring co-variant parameters. In order to allow X.M(...) the type checker has then to prove that the call is not polymorphic (like the reference X resolves strictly to objects of ClassX and not to any derived class at all) - no polymorphic cat calls.
- The trade-off is as follows: on the plus side this allows covariant parameter types and other changes incompatible with a strict notion of subtyping. It is kind of accepted from programming practice that contra-variant types, although theoretically desirable (no headaches for type theorists), have very little or no value in practice. No user of C++, Java or other language is ever known to cry publicly for lack of contra-variant parameters in derived classes. Many people though expressed the desirability of covariant argument types. On the negative side, typing with such a rule is safe, but it requires global analysis of the code, and second, adding a new class can introduce a problem in the base class or some unrelated code that uses the base class (one would normally expect that when you add a new class to the system, at best you get compilation/runtime problems in the class that you added). I'm no Eiffel expert, but I would expect separate compilation and dynamic loading to be problematic if they are supported at all.
The problem with the NoPolymorphicCatcalls? rule is this: Suppose you have a working system with a class B with some method m; B may or may not have subclasses. The code contains calls of the form B.m(). Now, introduce a new class D which inherits from B, and overrides m in such a way that D.m doesn't honor the contract of B.m (argument covariance being one way; the subject of this page). In other words, D.m is now a catcall; and B.m a polymorphic catcall (because a variable of type B may be bound to an object of type D). According to the rule, the call B.m() is no longer legal. Unless I'm missing, introduction of a new type into the system breaks the existing code.
And the analysis does appear to require knowledge of the entire system. In particular, it is needed to know which classes are leaf classes and which classes are not; adding new classes to a system can change a leaf to a non-leaf. Perhaps in the applications in which Eiffel has been deployed, this isn't a problem. Bbut in the component-software world where modules from different vendors are composed together, often at the last minute; this is a significant problem.
- This is exactly what has been said above, we agree entirely. But thanks for elaborating the point. -- Costin
The frustrating thing is that a simple implicit downcast (a runtime type check) would, IMHO, be a more attractive solution. Not ideal - Meyer seems to think Eiffel suitable for mission critical stuff, and thinks that runtime type queries are incompatible with this (he may be right) - but certainly better than crashing; and for many applications better than having to go modify previously working code to avoid the polymorphic catcall. Interestingly enough, the new "fitted" function he proposes in the paper you cite does precisely this. It takes advantage of return type covariance (which is typesafe) and does a runtime check on an argument of type GENERAL (how Eiffel spells "Object").
I'm sure that BertrandMeyer was aware of the issues before he made the design choice - I still maintain that for most applications it was the wrong choice. (And for mission-critical stuff where runtime type errors - trapped or not - are not acceptable; use of generics - F-bounded polymorphism, in particular - will work, without the problem of new classes breaking existing code.) I assume this is the solution that you refer to that KimBruce? described (an elegant way of doing this that is type-inference decidable is described in FoundationsOfObjectOrientedLanguages?).
Re: "An example where covariance is useful and correct is still missing": it is potentially useful and correct, ceteris paribus, assuming a hypothetical ideal, in precisely the common cases where the mainstream contravariant inheritance fails to allow for an obviously-correct model, for example the infamous "ostrich is a bird but cannot fly" and "ellipse is not a circle, circle is not an ellipse" examples. -- DougMerritt
What's obviously correct there? The CircleAndEllipseProblem precisely shows that there is no subtyping relationship between the two, it only seems to be there because of abuse of the words Circle and Ellipse. Classifying ostrich as bird is a different thing, it breaks the assumption that all birds fly. Either don't assume that in a program or don't label the ostrich as bird. If necessary, refactor the bird class, it's only software after all. When "just modelling the world", you have a problem indeed, but a broken type system won't help you, NonmonotonousReasoning? would. But trust me, you don't want that in a type checker.
- Again, you said it surprisingly well. The problem is about the unrealistic OO dream of "modeling the world" (domain) in classes, where domain concepts and conceptual relation map one on one into classes, inheritance and pointer aggregates ("association" in UML parlance). For some domains, it works beautifully, for a lot of domains it simply does not. I meant for a long time to write a wiki page on this subject. -- CostinCozianu
- (Now that's funny. You do realize that you're talking to the same person you labeled a troll just a few days ago?) Anyway, "modeling the world" is nothing bad, one just has to keep in mind that a model is a simplification and has to serve a purpose. A "general purpose model" ends up having no purpose at all. A particularly extreme example of that is the SuggestedUpperMergedOntology?, which in my experience is completely useless, because it's simply modelling for the sake of modelling. It also shows that a "model of the world" needs approximately the full power of PredicateCalculus?, which would translate to a TypeSystem with universally and existentially qualified and probably even dependent types. That would be quite impractical, and so "modelling the world" in a TypeSystem will always incur compromises.
- Another observation is that there are people who routinely create small models of part of the world to structure programs, called programmers. And then there are those who only create models and never programs, called software analysts. The later are often completely incapable of programming, but have a surprising inclination to teach "proper" programming. Don't listen to them, listen to programmers.
Both of you seem to have misinterpreted what I meant by my (ambiguous, I now see) phrase, "fails to allow for an obviously-correct model". I did not mean that there is some existing god-given model that is obviously correct, but which is not allowed. I meant that, of the models that are allowed, none are obviously correct (some are controversially claimed to be correct, which is not the same as being obviously correct to all). Quite a different thing. -- Doug
- You're right, I misread that. However, the model without any subtyping relation between circle and ellipse is obviously correct. There may be a more practical model that is also correct, but you see, that's a bit controversial ;-)
- No, it isn't obviously correct, it's controversially correct. Of the choices offered in most OO languages, it is more correct than subclassing one from the other, yes, but the thing is that in most problem domains, circles and ellipses are not unrelated, it's just that the relationship is not one of substitutability. So an obviously correct model would be one that doesn't try to pretend one is substitutable for the other, but does represent the relationship between the two, by something other than inheritance. People get all hung up on IS-A relationships, but in semantic modelling there are zillions of other kinds of relationships; they just don't enjoy any special kind of support in any OO language I'm aware of, whereas a variety of such things is universally offered by semantic databases.
- But there are no degrees of correctness, only of completeness. Anyway, all relationships beside IS-A are simply expressed as member functions. So after all the controversy is not at all an issue of typing, modelling, whatever, but an issue of people overusing and overrating IS-A relationships and inheritance. I honestly don't understand where that emphasis on inheritance comes from.
- Mostly agree. "When the only tool you have is a hammer, everything looks like a nail." However, there is a huge problem with expressing relationships via member functions, which is that there is no language support, it's all do-it-yourself with no safety net. In theory there's nothing standing in the way of a language directly supporting many relationships beyond inheritance, it's just not a popular thing to do - and beyond co/contra/in-variance, certainly not widely analyzed in terms of type theory. But it could be, and I claim, should be. (In semantic databases, this sort of thing is handled reasonably well by the addition of hand-crafted rules to error-check the use of various relationships.) And more immediately, contravariant languages could add compiler-checked optional covariant features, some of which can be error checked at compile time rather than relying on run time exceptions, which of course is highly desirable. I heard a rumor that Java may receive some such features in the future.
For a very practical example, it so happens that I even posted code on wiki, long before this discussion, that is the perfect setting for co-variance. See GridLayoutEx. Because JavaTypingWasSimple the protocol between Container, Component (the contained object), LayoutManager - the layout algorithm implementation, and Constraint - the data defining or constraining the position of the component within the container is loosely typed. it could be typed using covariant methods or using ML style parameterized modules (functors). Of course, these technicalities are beside the point of this page. -- Costin
AFAICS the only place where covariance seems to be useful there is the method addLayoutComponent, which could be declared to expect a Constraint instead of an Object. The right thing to do is to remove this method from the interface LayoutManager2. Having it there is a lie anyway, since no LayoutManager can accept any Object as constraint. Of course that would introduce problems for methods which just take a constraint of some sort and pass it on. Clearly, given parametric polymorphism, the problem vanishes. So this example does not apply to Eiffel. In general it is also solved by always grouping a LayoutManager and the appropriate Constraint together or by explicitly using existential (no need for dependent) types.
One of the problems is that in such cases parametric polymorphism is not enough as the actual types may end up being decided at runtime. For example in a truly ComponentBasedDevelopment a form editor may lay out components and serialize the result. Thus some actual types may end up being dependent on values derived from user input (or other external source not available at compile time).
To make a simpler case, imagine an event dispatcher who translates events from some underlying primitive mechanism (X, Windows, a log filter, etc), and calls event handlers for its subscribers. The protocol might be (using Java)
interface EventDispatcher? {
subscribe(EventHandler c, EventMask? filter); unsubscribe(EventHandler c) }
interface EventHandler { onEvent(Event e); }
And now an actual instantiation of an event handler can be:
ParticularHandler? implements EventHandler{
// this does not compile in Java, but illustrates covariant types
public onEvent( ParticularEvent? e);
/**
* the client of this class has the obligation to use only this constant
* to subscribe to an EventDispatcher?
*/
public static final EventMask? MASK= EventMask?.createMaskFor(ParticularEvent?.class);
}
Now the implementation of EventDispatcher
? has to dispatch Events polymorphically using the declared onEvent(), while the exact type of a particular subscriber from its generic collection of subscribers is dependent upon runtime values and cannot be abstracted at compile time. The client of this mechanism may prefer to use a covariant
and virtual method, because the signature onEvent(ParticularEvent
?) communicates most directly the design intention, while he assumes the burden that the correct EventMask
? is given to the dispatcher such that the "type unsafe" onEvent() is called at runtime only with values of correct type. A LISP multimethod wouldn't help here either because in case of a value of the wrong type it might select that general case, where what is really wanted would be a safe exceptions indicating the failure to match the expected types. Existential types are not likely to work since the particular type of the event has to be known to both parties (EventDispatcher
? and ParticularEventHandler
?). Of course, the above design can be materialize in current Java in many ways, but the most straightforward is to do a type cast at the beginning of the method:
public onEvent( Event e___ ) {
ParticularEvent? e= (ParticularEvent?) e___;
// use e from here
}
This illustrates a more generic problem with type systems: it is guaranteed that no matter how advanced they are, there will always be cases that they cannot handle, I don't know if they proved a theorem similar to the theorem of continuous employment for compiler writers but I wouldn't be surprised if such a theorem can be found for type systems as well. So language designers have to assume trade-offs. -- Costin
But there is a solution, in this instance - use generics: If, rather than having EventDispatcher? and EventHandler you instead have EventDispatcher?<T> and EventHandler<T>, and instantiate these on ParticularEvent?, the compiler will prevent you from ever inserting anything but a ParticularEvent? (or a subtype) into the event mechanism. As many have observed - often times, when you think want covariant inheritance, you really want generics (without any subtyping).
Not at all. One EventDispatcher? "object", has to dispatch to a dynamic number of handlers corresponding to a dynamic set of Event types but all deriving from Event. Now there you go, try one more time.
Of course, there are times where you have to use inheritance and covariance - in which case you must either use a typecast, or run the risk of UndefinedBehavior. -- ScottJohnson
Or have the runtime do the type cast for you as part of dispatch mechanism, in case that the actual method overrides covariantly in the derived class. That would have been the sensible solution that B. Meyer could have adopted.
There is still a way to implement the EventDispatcher? with parametric polymorphism, existential types and without covariance. To do so, the EventMask? has to be a function, not just a predicate. Such a function can return a different type, which would be the type the concrete EventHandler expects. The signature would be something like:
interface EventDispatcher? {
subscribe( forall T . EventHandler<T> c, T filter(Event) ) ;
unsubscribe( forall T . EventHandler<T> c) ;
}
It's important that filter can also return a null reference here. Of course I'm severely abusing Java's syntax. If this is to be implemented in Java, the filter function would have to be implemented as an Object and the forall qualification would have to be hidden by combining a EventHandler and a suitable filter in yet another class. The naive type system of Java is really of no help here. It might be far more practical to simply unify EventHandler and filter function, so it would have a completely generic handle() function that also told the caller whether it really handled the event. However, in a language with explicit universal qualification and parametric polymorphism, the above is type safe and works without runtime type checking.
No, I do not think you had all the requirements that I intended and you slightly restricted the problem to make it typable. I do not think that Java extended with existential types would be able to solve it. I do not think it could be programmed to be functionally equivalent and fully statically typed in any of Ocaml, ML, Haskell as defined by the latest Ghc. The Ghc solution would require monads for the setting is imperative and EventDispatcher? is intended to be connected to an I/O source for events. But I'd like to be contradicted nonetheless so I'll try to formulate it more precisely.
To make it more clear: objects of type EventMask? are opaque to EventHandler, the event handler simply declares what he's interested in. If this was ML/Ocaml, EventMask? would reside in the same module with EventDispatcher? not with EventHandler. Furthermore, actual Event objects are instantiated from an external source, and the compiler/linker may not even have access to all Event subtypes when EventDispatcher? is compiled - the function/method selection for handleEvent(e) has to involve runtime polymorphism. Here's more extensive Java code - of course java is more dynamic than ML/OCaml/Haskell because of its reflection facilities:
public class EventMask? {
protected abstract boolean accept(Event e);
/**
* constructor for the actual hidden type
*/
public EventMask? createMaskFor(Class evtClass) {
// dummy unoptimized implementation can be replaced by
// anything else without breaking EventHandler clients
return new EventMask?() { boolean accept(Event e) {
return evtClass.isInstance(e);
}};
}
}
public class EventDispatcher? {
List<Pair<EventHandler,EventMask?>> subscriber_list;
// ... code for deserialization, maintaining the list of subscribers etc
/**
* this method is connected to the input thread and dispatches events to handlers
*/
public void process(String input) {
Event e= (Event ) deserialize(input);
foreach << handler, mask in subscriber_list >> {
if (mask.accept(e)) handler.onEvent(e);
}
}
}
Now the supplier of a "plugin" package that can even be deployed dynamically, can have the following code.
ParticularEvent? extends Event { /*...*/ }
ParticularEventHandler? implements EventHandler {
static final EventMask? myMask= EventMask?.createMaskFor(ParticularEvent?.class);
// polymorphic covariance is useful here
onEvent(ParticularEvent? evt) {
}
// activation method to be called by a third party
public void doSubcribe() {
EventDispatcher?.instance().subscribe(this,myMask);
}
}
I didn't restrict anything, though I may have overlooked something (which isn't all that hard, longwinded as Java is).
Okay, I'll try to be as precise as possible. For the sake of argument, I'll stick to Glasgow-Haskell. I hope, the syntax is self explanatory enough. We'll need Haskell with explicit universal quantification (or maybe existential types, I'm never really sure which is which), and the Dynamic datatype might also come in handy. Of course, where IO is involved, the code becomes monadic. That doesn't matter much, code in Java is monadic anyway (though Java programmers generally don't want to know about that).
First, have a look at the EventMask? class. This is basically nothing more than a predicate on Events, realized by the accept() member. We make this explicit, EventMask? simply is the predicate:
type EventMask? = Event -> Bool
After evaluating the predicate and it is true, the Event is question has to be downcast to some ParticularEvent
?. If the predicate if false, there's no need for tha cast to be defined at all. So we simply unify the cast and the predicate into the EventMask
?, which is now polymorphic in the result type:
type EventMask? particular_event = Event -> Maybe particular_event
Now for the handler. Each
EventHandler basically consists only of the method handle(). We might as well say, it
is this function and nothing else. Handling an Event presumably involves IO, so we get something like:
type Handler particular_event = particular_event -> IO ()
What the EventDispatcher
?? Mainly a list of EventMasks
? and associated EventHandlers
?, plus functions onEvent and doSubscribe. It's a singleton, so I won't bother with an object and just define free functions onEvent and doSubscribe and I'm not concerned where the association list is to be stored. That doesn't matter anyway.
data Pair = forall p_event . Pair (EventMask? p_event) (Handler p_event)
doSubscribe :: EventMask? p_event -> Handler p_event -> [Pair] -> [Pair]
doSubscribe mask handler = (Pair mask handler :)
onEvent :: Event -> [Pair] -> IO ()
onEvent event = mapM_ try_dispatch
where try_dispatch (Pair mask handler) = case mask event of
Nothing -> return ()
Just p_event -> handler p_event
Only one thing is missing: what's an Event? In practice probably a sum type of all the possible ParticularEvents
?. Here we're just going for maximum extensibility and wrap a dynamic type. That makes implementing the concrete masks very simple: just extract the dynamic value. (Sorry for possibly mistyped function names or wrong types; I'm not not using the Dynamic stuff often enough.)
data Event = Event Dynamic
createMaskFor :: EventMask? p_event
createMaskFor (Event dyn) = fromDyn dyn
Haskell has no reflection, which makes an equivalent to your createMaskFor impossible. We get something else here: the type of createMaskFor may have to be declared at the point of use and the compiler will put in an appropriately typed fromDyn, which achieves the same effect as the reflection does.
So in conclusion, I think, this is all really equivalent to your Java example, and still well-type even without runtime casts. (fromDyn could be thought of as a downcast or as a case analysis, but types only need a runtime representation within Dynamic.) How to deserialize an Event will be left as an exercise for the interested reader.
Now have a look at the strange types. We have only on data type with an existential type, and the only thing we do with it is to extract both components to immediately compose them. We might as well compose them earlier and don't need existentials any more:
type GenHandler? = Event -> IO ()
doSubscribe :: EventMask? p_event -> EventHandler p_event -> [GenHandler?] -> [GenHandler?]
doSubscribe mask handler = (gen_handler . mask :)
where gen_handler Nothing = return ()
gen_handler (Just p_event) = handler p_event
onEvent :: Event -> [GenHandler?] -> IO ()
onEvent ev = mapM ($ ev)
E voila, again an equivalent program, no cast, no covariance, no existential types, only parametric polymorphism. (And notice how few monads, how short it is, how readable, how it almost cures cancer and how it attracts chix. But enough of the advertising...)
Granted, I took some shortcuts. There are no more "objects" and there's no inheritance. Encoding objects into Haskell is cumbersome (and mostly unnecessary), encoding advanced types into Java is cumbersome as well and above all purely hypothetical. So I'll leave it as is.
You took quite a lot of shortcuts, which makes it far from being equivalent, but instead typable.
- Event is now completely untyped (dynamic). While in Java the base class Event may reflect a common structure and common functionality that can be made useful. I don't know if the latest GHC has a form of extensible records that could be used to model this feature, but if it doesn't then the whole exercise is futile, see below.
- EventMask? is now explicitly known to the clients of EventDispatcher? while in Java it is an opaque token, this is an undesirable feature as it reduces modularity: the writer of the EventDispatcher? can no longer refactor EventMask? to be something else, without all the clients being affected.
- this is probably easily fixable, but it's important because it breaks the modularity of the protocol. doSubscribe has to be monadic and it isn't. Assume event dispatcher is permanently connected to an I/O source and maintains a dynamic set of handlers, the clients are not allowed to replace the dispatcher or any other stuff that would make them privy to the details of I/O event demultiplexing. Clients are only allowed to request addding or removing a handler, and the event dispatcher does this internally.
In the end the key that I see in your design is that you made Event dynamic - and now you cannot make Event the sum of all particular event types, because event types can be loaded dynamically into a system - with Java this is trivial. This is a slight of hand maneuver as you observed yourself: we can always add a bottom type, or the other way (the Harper way) we can always encode Scheme in an algebraic data type and a bunch of functions.
Other than particular examples (such as the LayoutManager related contracts) that are quite common in OO practice, it is very intuitive to understand why contra-variance is unnecessary while co-variance is frequent. Often times derived classes are more specialized version of the base classes. Since rarely a class can stand on its own, the collaborators, which often end up as method parameter types, are more specialized as well. It is quite counter-intuitive (and I don't think it has eve been shown in practice) that a special case needs more general collaborators (the contra-variance case).
Of course, this poses exceedingly difficult problem for type theorists to come up with a proper formalism to address this situation, and certainly type theory was not advanced enough at the time that Eiffel was designed. This doesn't excuse B. Meyer for not having inserted a runtime safety check - I frankly don't know what possible argument he has about that.
Specialized methods allow more general parameters. If they instead require more special parameters, they are no longer a specialization of the original method, but something new. It's not all that counter-intuitive.
I believe that this assertion follows the "modelling the world" philosophy that you just criticized. In certain contexts methods with covariant parameters can be viewed as specialization of methods.
Type systems are not a fixed world where only one perspective can be accommodated. It is more like lego pieces: a designer may prioritize this feature versus the other feature, make some designs more cumbersome or leave some features behind to address pragmatic concerns. That's why I found the statement that "TypeTheory concluded that [...]", objectionable. It sounds too platonistic for a branch of mathematics that is constructive par excellence. Type theory doesn't dissect the world of types like real analysis dissects the world of reals. Type theory instead offers either complete formal models (lots of them) or bits and pieces from which language designers (more often than not the same as the type theorists) pick and choose. So type theory never concludes that the world of types has property X, but rather "if you construct a type system having these features and these underlying assumptions the following important theorems (properties) will apply".
The underlying assumption behind a simply typed lambda calculus is that subtype values are substitutable in all contexts (from where contra-variance is derived). A language designer who wants to accommodate co-variance can say, we'll relax that assumption and say: subtype values are substitutable in all contexts except: <case A>, <case B>, and we'll take the following design decision for such exception (ignore them and let the program crash, introduce a runtime check, stick his head in the sane, etc). And this doesn't mean he runs afoul of TypeTheory.
Well, of course you can always "repair" TypeTheory by lifting types (adding a value bottom that represents an error) and simply returning an error in all cases that no longer suit you. (In fact, all type systems have to do that to some extent lest then become undecidable otherwise.) Whether that suits the callers of the method in question is a different thing. I certainly prefer methods that don't blow up when called, and the type checker is there to convince me that they don't. Before I put up with "a subtype is substitutable for a super type except when not", I'd rather ditch the static typing completely. You seem to prefer static typing and get a runtime exception in the exceptional cases. We differ on that, but at least we both don't want to put up with undefined behaviour...
And regarding "modelling the world", I actually prefer functional programming with parametric polymorphism. Subtype relations aren't all that important there. Once your programming language works well without subtyping, you start to realize that there aren't that many subtype relationships out there. So whenever you stumble over this "oh, that's only almost a subtype", it simply is no subtype at all and that's no big loss. Oh, and I look at the code when deciding what's a subtype, not at the things in the BigBlueRoom.
(OffTopic meta discussion below, DeleteWhenCooked)
Just to set the record straight, I have not been participating in these conversations until today's comment. I presume you know that, so I presume your comments are mostly aimed at the others who have been debating you, and you were just using my little comment as a springboard. However, I personally am interested in these technical details. -- Doug
Costin, I gotta ask:
Why on earth are you so worked up about the original content of FamousLanguageTypeErrors? (which, after being refactored away, you've apparently re-introduced and continue to flog at on several pages)? Is BertrandMeyer your uncle or something? It is hardly the most inflammatory thing on Wiki; compared to many of the criticisms to be found here, it's pretty tame. Some of the tone can be softened, certainly - but I'll stick my neck out here, and note that you're frequently far more inflammatory in your dealings with others - and I'm certain numerous others will agree with me on that point - that it's deliciously ironic to see you complaining about chest-thumping criticism written by someone else.
(Plus, given BertrandMeyer's well-documented history of offensive and obnoxious pronouncements - many of which would make even EwDijkstra blush - many of us felt a bit of schadenfreude at seeing his nose rubbed in the dirt. When someone publicly conducts himself like an ass; he deserves to occasionally get treated like one.)
You might also consider that many of us, myself included, don't post anonymously to avoid criticism or responsibility for our words; but instead to donate them to the wiki. EgolessWiki and all that. You seem to regard anonymous posters with contempt (or at least suspicion), but for many people it's the preferred way to post.
-- ScottJohnson
Scott:
- first, I couldn't care less when somebody, even you, posts general fuzzy accusations of the nature "you're the same as RK" towards me. I consider them another bad form of critical style, when you "criticize" somebody so fuzzily that you deny any plausible defense. If you see something written by me that's unjustified you should express your concern then and there. I do not think I ever failed to take responsibility for what I write. As for your "numerous others would agree", I'd suggest to you that that's a mighty fine piece WoodenLanguage rhetoric.
- second, I do not follow your logic that if B. Meyer might have engaged in a little chest thumping of his own (vis-a-vis C/C++ programmers) then he should be treated in kind, on wiki of all places. I know that you'd like your wiki to be something like a virtual cocktail lounge where almost anything goes. But frankly, I could not care less about your "schadenfreude" and I'm not going to start my next contribution having that in mind. Get used to it. And I humbly submit to you that if you "occasionally" treat B. Meyer "like an ass", than you might look "like an ass" yourself. "Occasionally", of course. And almost always when you refer to Dijkstra as an yardstick for blushing.
- third, when you see me mount an attack - either explicitly or implicitly - against anonymous posting in general, bring that argument again.
I'll believe in
EgolessWiki when I'll see one. I, myself, contribute anonymously when I'm presenting matters of fact, stuff that anybody with experience in the field could write equally well or better. When I know there's even the slightest chance to be wrong, have an incomplete perspective on stuff, or present highly personal opinions or value judgements then I know that I should better sign. I appreciate donating knowledge and effort to wiki, but donating material for TitillatingEgo
? (schadenfreude and all that) anonymously, I frankly do not appreciate. As for this not being the worst case on wiki, I am perfectly aware of that. Thanks, in part, to benevolent bystanders (PeanutGallery
?) like you, we do have worse. I occasionally deal with such things in a non-deterministic order - and I'm not the only one, but I certainly look like an attractive target. --
CostinCozianu
You are reacting far too strongly. ScottJohnson is by far one of the more generally reasonable people here, but you are rejecting his input out of hand, and even claiming "we do have worse" due to people like him, which is really going too far. The wiki is a better place for his contributions, he's not just some random clueless kibitzer! Disagree if you must, but distinguish the clueless from the clueful when you do so, please, with more respect (or at least less disrespect) for the latter kind of people. -- DougMerritt
I stand by everything I wrote about Scott, and I never implied he was a clueless kibitzer. That makes him all the more responsible for the non-sense he signs on. -- Costin
Well, still, you're calling his comments nonsense (which to me sounds like it implies a judgement of "clueless", but I don't want to argue the point if you disagree, although feel free to clarify if it would help promote communication), whereas I think there's reason to recognize this as one of those cases where reasonable people can disagree. Both you and he use personal judgement as to when to sign posts versus when to leave posts anonymous, after all; neither of you is 100% polarized on the issue. So it seems to me that your disagreements on that particular topic are about which situation calls for which. -- DougMerritt
Yes, I'm calling one particular comment nonsense as a matter of logic: the "B. Meyer is an ass" line of reasoning is a non-argument. And as a matter of style, I find it simply in bad taste, especially aggravated by a gratuitous reference to Dijkstra. Maybe Scott was too busy trying to administer a counter punch to yours truly and forgot to double check if what he was trying to communicate and the way it ended up in the form of English text on a wiki page were really the same thing.
I do not think I and Scott disagree on using signatures, most quality contributors follow the guidelines I mentioned, and the guidelines are in themselves quite flexible. I just made it clear that it never crosses my mind to attack anonymous contributors in general, but I do have a problem with folks who deviate from this wiki tradition about when to sign and when not to, especially when they use the cover of anonymity to mount dubious attacks.
And hey, this is wiki, everybody makes mistakes, including possibly me in my response to Scott. He can defend his points, he can delete everything, he can delete parts of it leaving what is relevant. Or leave the whole clutter here. -- Costin
Of course, I didn't say that BertrandMeyer is an ass; I suggested that occasionally he acts like one. To me, there is a difference - just because someone (whether you, me, or Meyer) is a published author and a well-recognized authority on a subject, doesn't give them a license to escape rebuke when their public comments become undiplomatic. At any rate, it's no big deal; I'm prone to act like an ass myself, and may have done so on this page. We all act like asses sometime - and often times the person who acts like an ass is the person who is the last to know about it; that's human nature.
OTOH, I have yet to act like an ass in the published literature - whereas Meyer has published diatribes such as BewareOfCeeHackers, which have earned him well-deserved rebukes from other CS notables. And my reference to Dijkstra isn't without merit. While one must recognize the numerous contributions that Uncle Edsger made to the discipline, one must also remember that Dijkstra was quite prone to publishing statements in the literature which are essentially flames, even if articulately expressed.
- Your original English was: "[...] well-documented history of offensive and obnoxious pronouncements - many of which would make even EwDijkstra blush". I'm not a native English speaker and I might be wrong on this one, but the logic of the text implies quite directly that Dijkstra also had "offensive and obnoxious" pronouncements of his own, and not of the incidental or inconsequential kind, since he was found worthy to be a reference by none other than the honorable ScottJohnson. I do not know how you counted in Dijsktra's published writings (because some of his private correspondence was made public after his death and should not count), how you weighed the technical contributions against the allegedly offensive and obnoxious, and how you found Dijkstra worthy to be a reference, but if I were you I'd have waited first for some of his colleagues with comparable stature (Hoare, Knuth, etc) to make a reference of this nature, lest I fancied the risk of looking like an ass myself. Of course, none of his colleagues would come out with such considerations in public, which is precisely the point. And even if after a long and careful examination of Dijkstra's public writings I would still conclude that he was guilty of offensive and obnoxious pronouncements and worthy to be used as a yardstick, I would still think that expressing such on wiki hardly serves any useful purpose. Oh, wait, one useful purpose I can think of is that this comparison itself could be used as an yardstick for future reference. -- Costin (sig added by Doug to help disambiguate who is saying what)
- Sorry, Costin, but you picked the wrong thing, there, to critique Scott about, and are simply showing your ignorance of Dijkstra on this subject. He is extremely widely famous (infamous) for his flames, during his lifetime, not private stuff dug up after his recent death, and Scott's comparison of Meyer and Dijkstra was extremely apt in that sense. This is easily checkable, you don't have to take my (our) word for it, however I do have a bunch of Dijkstra quotes that are each mini-flames, if you happen to be interested.
- Correct; I was referring to comments that Dijkstra published during his lifetime; and am unaware of any juicy stuff that has been made public now that he is no longer with us. Of course, as indicated above - I've been known to stick my foot in my mouth at times, so perhaps I shouldn't be too sanctimonious on the point. (It should also be noted that many of Dijkstra's criticisms - of such languages as COBOL and BASIC - were FTMP correct; it's just that he chose to pen unscientific things like "using BASIC will irrevocably rot the mind", and I am paraphrasing very roughly here). -- sj
- There are other aspects of your argument that are not invalidated by that, and I don't want to get involved in the other issues - this particular thing is a matter of fact, not opinion, though. -- DougMerritt
- Matter of fact only if you play HumptyDumpty. You both have neither the arguments nor even the standing to even contemplate justifying the usage of terms like "obnoxious and offensive" next to Dijkstra. It's a matter of fact that Dijkstra's criticism of parts of the industry and the academia was quite blunt. Not even Dijkstra would have denied that. He himself chose the title "how do we tell the truths that might hurt" for one of his most severe papers. Blunt criticism sometimes can hurt like good medicine sometimes hurts, and incidentally he also had a private note "on hygiene intellectual and otherwise".
- But picking adjectives like "obnoxious" and "offensive" is not a matter of fact but a matter of latitude, or more precisely of lack of discernment and ignorance of basic social norms. I seriously doubt that Dijkstra would have admitted to any of his published writings being obnoxious. As such, and given there's no consensus in this regard that you can refer to, given that he's a great figure now passed away, taking the liberty of using precisely those words (and need I remind you that Dijkstra's English was exquisitely precise?), and then going at length to justify it, is simply unconscionable.
- If such characterization would have been an objective reality, rather than your personal spin, how in the world did his alleged obnoxiousness get past the editors? It's not like Dijkstra published in any kind of place where he could have gotten away with the kind of obnoxiousness that you occasionally get away with here. -- Costin
- Your position here is not defensible. Consider two of his most famous quotes along these lines:
- "It is practically impossible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration." -- Dijkstra
- "The use of COBOL cripples the mind; its teaching should, therefore, be regarded as a criminal offence." -- Dijkstra
- You are not going to get any mileage out of trying to claim that these two quotes are not "offensive"; they plainly were intended to be offensive to those two languages and to their users. That's not "personal spin", and if you keep arguing otherwise, you will merely come across to all readers as being disingenuous.
- This line of argument is also completely beside the point: Scott's comparison of Dijkstra and Meyer is, as I said, extremely apt, no matter how you label their critiques. So what if you did manage to convince Scott to change his description of these diatribes from "obnoxious" to "critique" or some such? His basic point is unchanged: live by the sword, die by the sword. Offer strong critique, expect to receive strong critique. -- DougMerritt
- Do you preemptively accuse me of being disingenuous, so that I can no longer respond to your completely wrong and misguided argument? -- Costin
- No, sorry it sounded that way. My criticism was intended to be straightforward, not to act as a cheap rhetorical ploy of that sort. Sorry it sounded otherwise. It seemed to me that you were using a rhetorical strategy that involved a certain kind of bluff, and I was attempting to call your bluff, to shift the conversation to a more direct line. I may or may not have misperceived where you were coming from. This thread has grown stale with time so it doesn't matter. -- Doug
- It was a cheap rhetorical ploy, the text above is what it is and cannot be plausibly interpreted any other way. Maybe it was not intended to be that, but that's another matter, the wiki text does not record intentions unless the author does so explicitly. This discussion went nowhere because you guys are simply in the wrong and it is so rare the case that anybody on wiki admits being wrong, more often the discussion automatically grows stale when one of the parties no longer feels the comfort zone to plausibly claim to believe it is in the right. And admitting to soemthing being wrong is automatically equated with being taught a lesson and a decrease in some kind of non-existent stature. So the ego factor grows bigger and bigger, as if the said stature is something really important, and worth defending from imaginary calamities. That's the recurring pattern of wiki "debates".
- If this was a debate over small potatoes, it didn't matetr. But that Scott and Doug want to accredit the idea that Dijkstra is widely acknowledged as an obnoxious writer is simply preposterous. If you pay attention this is very different than to claim that Scott and Doug think themselves that Dijkstra was an obnoxious writer. The latter claim is entirely subjective and you're both entitled to being opinionated, I wouldn't even dream to waste my breath copnvincing you that you are wrong. But the former is a very simple and objective matter, and can be verified (nevermind that you did do a little ShiftingTheBurdenOfProof), I've given you a very simple experiment (google for the title of the paper from where you extracted the alleged offenses, and see if references are good or bad, or if anybody at all mentions it in connectiopn with Dijkstra's alleged obnoxiousness). So nevermind that you think (in my opinion wrongly) that Dijkstra was obnoxious in such occasions, the simple fact of the matter is that you have no supporting evidence to show that your opinion is widely shared by the people who matter.-- Costin
- Is it me, or is this debate getting out of hand? The discussion of Dijkstra (who wasn't mentioned in the original FamousLanguageTypeErrors?, IIRC - just in my post above) seems to have grown longer than the discussion of the original topic. FWIW, I didn't - and don't intend to - accuse Dijkstra of being an "obnoxious writer" in the whole; again, you act as though I've uttered some unspeakable and vile slander against the man. Some of the things he has written can be construed as obnoxious, certainly - but big deal. We all have our days, and I've written lots of obnoxious things myself. So has BertrandMeyer, so have you, and so has Doug. The fact that you would grab onto one throwaway phrase and hold onto it so tight, like a dog shaking a chew-toy in its mouth, I find fascinating. -- ScottJohnson
- Gee, Scott. Are we speaking the same English language here? If I "grab and held it so tight", why wouldn't you let go? I simply protested one very objectionable phrase of yours, and you could have deleted it, you can still delete it together with this whole shebang that you protest. But it wasn't the case, it was the case that both you but more so Doug, escalated and aggravated the initial error. Here's a quote from one of the paragraph - says Doug: ''"Sorry, Costin, but you picked the wrong thing, there, to critique Scott about, and are simply showing your ignorance of Dijkstra on this subject. He is extremely widely famous (infamous) for his flames, during his lifetime, not private stuff dug up after his recent death ... "'. And the next paragraph you start with "Correct. [...]", and so on, so forth. Now if you can logically square this one with what you claim your and Doug's intentions were, I am more than fascinated, I am speechless. What's the big deal in doing the right thing [TM], which would be the removal of the objectionable content together with this useless escalation? But simply declaring that (both) your intentions were this and that (noble of course), and accusing me of escalating, when your way with words leaves very little room for interpretation is not going to cut the muster. Yes, everybody makes mistakes, and there's no shame in correcting them, that's partly what friends are for see CriticsAreYourBestFriends. The real problems arise when some get to be extremely perseverent. Frankly, I would have preferred to have been possible for me to simply remove your phrase, without having to convince you it was an unfortunate and inappropriate expression, but I rather doubt this would have made any less comotions, so the honor of doing the right thing rests with you.
- WasDijkstraObnoxious?
- Dijkstra's statements referred here, were not "intended" to offend. Dijkstra's style and mastery of English were extremely good (in fact probably an order of magnitude better than the majority of native speakers publishing computer sciency stuff). Now that I think of it, your understanding of the meaning of the word "intend" and the related "process of intentions" could use some improvements. Yes, take them out of context, they might look offensive, even obnoxious. Put them in the proper context (HowDoWeTellTruthsThatMightHurt) and you only have a case if your understanding of the matters of English style and composition is at the level of talk radio audience (for lack of better comparison). Dijkstra was extremely humble, and it is precisely that humility that allowed him to be bluntly honest and to use such figures of speech to convey a grave and serious, but nevertheless important message. But now it turns out he might not have considered the possibility that anybody at all [eliding the truly deserved epithet here ], empowered by the TragedyOfTheCommons that is WebTwoPointOh can now casually pick on out-of-context quotes, as if the subject was RichardKulisz or TopMind.
- Unlike Dijsktra's statements, Scott's comparison had no context, it had no possibly redeeming purpose, it was just casual. As if taking Dijkstra as an yardstick for offensiveness and obnoxiousness can be done casually.
- Why not? Dijkstra was a damn fine computer scientist, but he's not God. Perhaps the issue here is not that I have spoken ill (even if in a relatively minor fashion) of someone, but that I have spoken ill of an authority? I suspect, Costin, that if I were to write on this page "RichardKulisz is an asshole", that you wouldn't object (and would in fact agree, as would Richard). But even the relatively minor complaints against Meyer and Dijkstra on this page, you react to as if I had just slandered these two fine gentlemen in the most foul and heinous way possible. Is that the issue, that I saw fit to criticize two individuals who I will readily agree are superior to little ol' me in the pantheon of ComputerScience? While CS, like any scientific discipline, should be based on merit; I thoroughly take issue with the notion that I have no right to criticize Dijkstra or Meyer. Especially when the criticism I offer has been offered many times before, by many other observers. -- sj
- You have the right to criticize, all right, and it was never contested. And I have the right to tell it like it is, which in this case is anything but criticism. If in a further unrelated discussion with you not being present, I casually drop something like "some of the crap X said would make even ScottJohnson blush", you wouldn't call that "criticizing", would you? Please note the implication that there would be some other crap that wouldn't make Scott blush. Now with respect to your later claim, I am highly skeptical that you'll find many "other observers" who have casually dropped "obnoxious" and "would make Dijkstra blush" in the same phrase. -- cc
- You might be shocked to find out now that it cannot be this way, so now you have a problem on your hands. Such a reference and your defense of it can be judged gratuitously offensive and obnoxious. And now what? Are you going to put Dijkstra on trial to establish that he was really obnoxious according to some unspecified standards, that incidentally neither you, nor Scott could possibly measure up to? Even if he was living he wouldn't bother to respond to such crap. Such non-sense can not reflect bad on Dijkstra, it can only make you and the C2 community look bad.
- Why would I bother with a "trial"? I offered an opinion; others can take it or leave it as they see fit.
- Not you, but Doug. He would bother to establish his matters of fact. On the other hand, you could keep more of your opinions private when they add neither insight nor value to wiki. Or recognize unfortunate expression of your opinions sooner rather than later and try to reword them. -- cc
- Let me recap it for you: judging Dijkstra to have written "obnoxious/offensive/flaming" texts is a matter of personal interpretation and value judgement, not a matter of fact.
- Agreed. Yet you seem to be extrapolating my statement in every which way, and treating it as a falsifiable claim which must be defended.
- I'm sorry for your confusion. The falsifiable claim belongs to Doug. Indeed you only offered opinion, albeit quite obliquely as a side-effect. -- cc
- Merely the fact that I disagreed vehemently with a matter of moral/ethical/stylistic judgement should have given you enough of a clue to you that your judgement is not a matter of fact. Moral judgements for anything lower than crimes are inherently subjective. You both are entitled to your private opinions about Dijkstra, including the negative ones, but pretending that they reflect matters of fact, and talking as if they were premises that everybody should subscribe to is disingenuous and only ignorance can give you some excuse. You are now enlightened that not everyone subscribes to it. Even you two should be able to make the difference between the opinion that "Dijkstra was offensive/obnoxious/flaming on occasions" and the opinion "I could use Dijkstra as an yardstick for offensive writing.". If you can convince people that the former is an honest and reasonable opinion on which we can agree to disagree, the latter is extremely dubious. -- Costin
- Ultimately, I'll utter words that I think are appropriate. You may not find them so; but that's all right. You've said many things here on c2 that I thought intemperate and/or out-of-bounds, and I've learned not to let it bother me. -- ScottJohnson
- That might be true, but on one hand I loose my temper with the likes of topmind, RK, maybe DL, and the now gone RA and chicago.net. On the other hand, if you could unlearn the "not to let it bother me" part, I could then learn to bother you less. I don't know if I can extend this offer to all the people that I do bother.
Let me give you a few real
matters of fact for comparison, just to get you back in touch with reality and with proper English usage vis a vis "matters of fact":
- HowDoWeTellTruthsThatMightHurt has gone through the editorial process of Springer Verlag and, immediately after, that of SIGPLAN. The two organizations are not exactly known for publishing "obnoxious and offensive", "flaming" or other kinds of improper texts.
- The paper was taken very seriously. Follow ups (both pro and counter) ensue. Even texts dealing with technical issues cite it (the paper was strictly about the methodology of science and education).
- As the situation stands today, I googled for "how do we tell truths that might hurt" I was able to find no negative reference to it in the first three result pages; you can find plenty of positive references to it instead. Oops it turns out that the wiki page itself is negative. Isn't it nice that GoogleHatesWiki now? Ward is truly a wise man. -- CostinCozianu
At any rate, I will restate for the record that I still consider Meyer one of the leading authorities on OO; and ObjectOrientedSoftwareConstruction is still, IMHO, the definitive work on the subject. -- ScottJohnson
- Setting aside matters of taste and style, and the formal ceremony of sweetening the pill, I still do not get the basic argument. If Meyer went overboard in some contexts, why is it a good idea to allow a wiki text to respond in kind? After all that's what some parts of the UseNet and SlashDot are for, no need to consecrate on C2 wiki as acceptable politics. -- CostinCozianu
- Probably the best course of action, then, is simply to refactor. Don't belabor the point. At any rate, rereading the above, it appears that the harshest criticisms of Meyer in the original was the characterization of covariant overriding as a "blunder" (and the way he implemented it certainly was, IMHO), and the characterization of the no-polymorphic-catcalls rule as a "brutal hack". Both comments strike me more as blunt criticism of Meyer's work, which should be fair game, rather than of Meyer himself. Someone with your resume in chess should know that accusing a grandmaster of making a "blunder" doesn't mean he can't play chess - just that he screwed up that particular game bigtime. But we're starting to split hairs here. -- ScottJohnson
- I think that Scott's response here is perfectly reasonable, and returns to the main point - I see no point in further terminological hair splitting. -- DougMerritt
- Yes, if you pick up one word that might be borderline acceptable. If you pick up the rhetoric of the original version as a whole and compare it with the now refactored LanguageTypeErrors, it might be less so. I did refactor in the end, but I'd rather have the original contributors of a page do wholesale refactorings.
DecemberZeroFive
CategoryTypeTheory