That’s going to sound like a pretty radical statement to all the programmers reading the blog post but I’m not joking. Supposedly Philip Wadler, one of the main people behind Haskell and one of the designers of Java generics was overheard saying this. I can’t vouch for whether this or true or he believes it but it makes me feel slightly less insane to think that he did.
Before getting into this let me say that subtyping should not be confused with separating implementation from interface, which is pretty fundamental to a good programming language and can be achieved in other ways.
Subtyping is a very seemingly nice way to do this and it’s not hard to see how it has become so pervasive. To make something into a feature of a type system you have to actually formalize it and formalizing subtyping turns out to be horribly, horribly messy. If the messiness were constrained to the compiler, this wouldn’t matter but it causes problems for users of the language and leads to some unintuitive results.
Say A is a subtype of B, is a list of A a subtype of a list of B? <sarcasm>As I’m sure everyone who isn’t familiar with this, would intuitively guess, the answer is yes if it’s an immutable list and no if it isn’t.</sarcasm> It doesn’t seem to me that the type system should have a priori knowledge about what is mutable and what isn’t so this is already a problem. Some languages such as OCaml and Scala would let you declare that your list class’s type parameter is “covariant” allowing your list of A to be a subtype of a list of B, since you know your list type is immutable. Is Joe Programmer ever going to understand or use that feature though? Java 1.5 actually has the best solution to this problem out there, this is the technical reason to declare something which might be a list of A or a list of B as a list of “? extends A”. This is the part of learning generics I’ve seen the most confusion with though. Since Java has subtyping doesn’t A implicitly mean “? extends A”?
Another example is the equals method. How much boiler plate code have you written figuring out the run time type of equals’ argument? On top of it no matter how your wrote it it’s broken in one way or another. If your class has subtypes, either your equals method violotes reflexivity or the the Liskov substitution principle. If it can potentially return true for an argument of a subtype, it is almost certainly not reflexive (it can be if the subtype’s equals ignores the added fields but you don’t generally want that either). If it can only return true for arguments of exactly the same type it’s not properly substitutable. Imagine I have a set of cartesian points and point has a subclass colored point. I can have the point (0, 0) in my set twice because one of them is blue, but there’s a good chance that’s an error if you’re trying to make a set of cartesian points.
Wouldn’t the world be simpler place if equals could be specified to take an argument of the same type as the class declaring it? It would be straight forward to implement and consist of nothing but comparing fields. It would always be reflexive unless you were trying to write a broken equals method on purpose. Of course if it could the inheriting class wouldn’t be a subtype anymore! If I try to upcast it to its base class something funny has to happen because it doesn’t have an method equals which takes an argument with the type of the base class.
I’ll talk more about what is actually in my langauge soon, I thought this would be a good starting to tear down your assumptions about how things will function first, the enlighten you as to how they do. I don’t know how many people I will have actually convinced that subtyping is a misfeature, especially without presenting an alternative. I hope I at least got you thinking about programming language design in a way that you probably haven’t before.