Inheritance dunrite

I am absolutely surprised at the number of failed examples of inheritance. What troubles me is the extent of ignorance prevailing on the subject of inheritance. Every once in a while, I read an old rant by a Blub programmer making claims to an understanding of a topic while simultaneously proclaiming that there are so many others without this understanding. Less often, I am compelled to write something myself.

I will start by using Blub grammar and syntax, but try to keep the usual euphemisms and loaded words to a minimum (I have two, annotated with warnings).

Look at the following interface declarations:

interface I {
  T i();
}
 
interface J {
  U j();
}
 
interface K extends I, J {}

If a method accepts a type K, what that method really means is that it accepts a type that is composed of both type I and J, which is more general than type K. I am ignoring the fact that interface K may have some additional restrictions that are not enforced by the type system, because well, this is Blub after all and using type systems to denote formal requirement specifications is well beyond Blub. If you have a type L that is composed of I and J, you will not be able to pass it to the method. Instead, you need to write an “Adapter” (WARNING: euphemism) from L to K.

I now propose a hypothetical language (still Blub though) that allows you to pass a “I with J“, which means it might be a K or it might be some other composition of I and J. Note that a compiler can not deduce that K is composed of I and J and generalise this type, since this requires the compiler to compile I, J, K and all compositions of I and J, which is potentially infinite. In other words, time goes this way —>, not that way <–, despite what the Blub type systems say.

In my hypothetical language, you do not explicitly give method arguments types. Instead, if you call both i() and j() within the aforementioned method, the compiler or type inferencer will determine that the method argument has type I with J. Surely, you have seen software in Blub languages, where you pass a type with a bazillion methods and only one or two of those methods are used when it is passed? It’s a problem, right? Ever used one of those “mock frameworks”? (WARNING: euphemism for “workaround”). Have you ever passed a K and only called i()?

In my hypothetical language, if you only call i(), then the method argument type will be inferred to I, not K, not even I with J, since remember, we do not explicitly annotate types on method arguments.

Now, is my hypothetical language really hypothetical? No! You see, there are (at least) not-so-crap type systems out there that permit this level of flexibility. They are far more powerful than this little rant purports, but you’ll just have to take my word for that. What I have effectively described is (one aspect of) Type Classes from a language called Haskell, which can also be used in Scala and other languages with at-least-reasonable type systems.

In other words, inheritance dunrite is as easy as putting down those languages that have got it all wrong and stop pretending that they are sensible or “industry grade”. Stop trying to understand their inherent contradictions through cognitive dissonance, write books about the nonsense, invent design patterns, refactorings, workarounds and mock this and that and framework foo and blah blah blah (can I breath now?). Furthermore, standing on a pedestal and proclaiming that “I know something that you don’t so na na!” is really quite silly, even if it is true (which it isn’t more often than not as has been seen!).

There is much more to inheritance than even the most expert Blub programmer can possibly imagine. I have only just touched the surface by introducing type classes here.

12 Responses to “Inheritance dunrite”

  1. Ricky Clarkson Says:

    Your favourite Blub language can do this, albeit verbosely.
    &nbsp;&nbsp;&nbsp;public &lt;T extends I,J&gt;void method(T t);
    accepts anything that extends/implements both I and J.
    And in dynamically typed languages, even I and J are unnecessary. If you call a method on an object, and the method doesn’t exist, then there is a failure at runtime.

    That sounds dreadfully unsafe, until you consider that this lets one work ‘naturally’ with types that are unknown at compile time (e.g., when making two separate codebases run in the same runtime). It’s less safe, but more flexible, and puts the programmer in charge.

    You could build a static checker for this - instead of inferring possible typeclasses based on method calls, it would infer possible concrete types based on method calls. If a method calls obj.i() and obj.j(), then obj must implement i() and j(). That can be checked at compile time, but again, there are cases where you want to say “I know what I’m doing, don’t check this statically”.

    Even quite strong type systems, such as Haskell’s, provide a way of punching out of the type system, of saying “I know what I’m doing”. My hypothesis is that the default should be “I know what I’m doing”, and I should be able to invoke the static checker when I want my code to be checked.

  2. Tony Morris Says:

    Yes, you can solve the composition problem by making every single method polymorphic on its parameters, but the problem of making the type too specific remains.

    There is no such thing as “types that are unknown at compile time”. There is also no such thing as “punching out a type system” (after all, Haskell’s dropWhile and takeWhile have the same type; do they “punch out of the type system”?). I refer again to djinn.

    I agree though, that the default is “I know what I am doing”, which is precisely the reason to use a type system. There are never times when you say “I know what I am doing”, when you don’t really know and you don’t want someone (or something) to tell you so.

  3. Ricky Clarkson Says:

    How does the problem of making the type too specific remain? &lt;T extends I,J&gt; does not suffer from the same problem as K and L, because an instance of either K or L fit T.

    Types may be unknown at compile time - they may be generated at runtime, or there may be no compile time, depending on the language.

    unsafePerformIO can reasonably be said to be punching a hole in Haskell’s type system - it let’s you get at the IO monad from within a function whose signature doesn’t include IO - i.e., it lets you get at something that you weren’t passed as a parameter.

    About knowing what you are doing - it is possible to know what you are doing but be incapable of expressing what you’re doing to the compiler. That may be your fault (not understanding the details of the type system your language enforces), or the type system’s fault (you know I can give an example of a valid program untypable in Haskell). Either way, it’s possible.

    Instead of trying to force you to type your program correctly, a language should facilitate that. That’s a language intended for people who know what they’re doing.

  4. Tony Morris Says:

    “Instead of trying to force you to type your program correctly, a language should facilitate that. That’s a language intended for people who know what they’re doing.”

    How can you not see the contradiction here?

    Let me rephrase it for you:

    Instead of trying to force you to do X correctly, a language should facilitate … people who know what they’re doing [instead of doing X incorrectly].

  5. Dave Clarke Says:

    Sounds very much like you are talking about subtyping, or just typing, and not inheritance. A good type system, such as Scala’s is very important, but so is the mechanism for plugging together bits of code. Scala scores highly here too, adopting traits along with good old fashioned inheritance. Inheritance is about code. One class inherits code from another. Types are a different matter, and in a good language, they are orthogonal. Scala’s new structural subtyping further separates subtyping from inheritance.

  6. Reinier Zwitserloot Says:

    This isn’t inheritance at all; this is composition. Actually, let me refrase: While ‘inheritance’ is still presupposed to have a very specific meaning, years of usage of the term in the wrong context has effectively de-attached the ‘proper’ meaning from the word. This is kind of like how these days ‘democracy’ and ‘republic’ are no longer separate concepts.

    Instead of trying to ‘fight’ a changing language, I just ask any discussion on such topics to be specific about what they really mean. Hence my request: Do not continue to discuss these topics with such a vague term as ‘inheritance’. Be specific. Do you mean implementation inheritance, or signature/type inheritance? Those are very different things.

    Secondly, I don’t think you understand what ‘blub’ means. ALL languages are blub. Even Haskell. This is another example of term which is rapidly changing definition. These days ‘blub’ is often used as a euphemism for java, or any other language deemed disdainful to the author. Ironically, usage of ‘blub’ in this way actually means the author utterly failed the point of the blub exercise and is a blub programmer.

    Back on topic: The problem with inferring type from method names is namespacing. i() is hardly unique. You can solve this problem by namespacing the method names but this gets tedious very quickly and hardly seems to be an improvement over attaching method names to type names implicitly, and namespacing the types. This is java’s tactic. Of course, easy ways to compose types (something java can currently only do in generics definitions, not type definitions themselves) is definitely a useful language feature.

    Internally java also uses composition on type parameters directly. For example, the tertiary operator has an internal composite type; e.g:


    interface A {}
    interface B {}
    class C implements A, B {}
    class D implements A, B {}

    A a = (someBoolean) ? new C() : new D();
    B b = (someBoolean) ? new C() : new D();

    is legal, because the type of the tertiary expression is (Object &#38; A &#38; B). It’s unfortunate that you can’t actually have explicit types of that format (except in generics definitions). This is a spinoff of Sun’s insistence on source and binary backwards compatibility. An annoyance to be sure, but it’s one of the reasons why java is considered ‘industrial’. Contrast with the other end of the spectrum, e.g. PHP which makes breaking code changing in 4.0.3 to 4.0.4 type updates.

  7. Kirit Says:

    I’ve written up what I think you’re talking about in terms that maybe us “blub” programmers can understand.

    http://www.kirit.com/Inheriting%20type%20safety

    As Reinier points out, I suspect you’ve mis-identified the aspects of a language that make it “industrial”.

    A good type system is only one factor, and not an especially important one to most. Partly this is because many don’t really understand what a type system can do for you, but also because many choose not to use one, presumably because it solves a different problem to the one their software must.

  8. Tony Morris Says:

    I can only conclude that someone who thinks that a type system is unimportant doesn’t really know what a type system is. I humbly excuse Mr. Zwitserloot, because he often comments on what he doesn’t understand (not that this is a bad thing, but I expect it contributes to his preferred learning method).

    Furthermore, nobody doesn’t use a type system. Even Lisp et. al. There is simply no such thing (this is clear if you know what a type system is).

  9. John "Z-Bo" Zabroski Says:

    I think you are being impolite to your readers who take the time to comment on your blog. Just be quiet and listen to your readers, and you might learn something.

    I thought Reinier Zwisterloot’s reply was rare quality and cannot comprehend why you insulted him. Moreover, a personal attack is unnecessary.

    Come to that, I related to what Reinier was saying. Everything he said was correct, and at no point did he intimate that a type system is unimportant.

    Any programmer worth their salt knows type systems are important. Can’t we hear Bruce Eckel chanting somewhere? “It’s about the types” was Eckel’s war cry more than a decade before Benjamin Pierce wrote his magnum opus! Eckel was teaching from this perspective since the 80s, and eventually became famous for it in the 90s. Unlike many authors, Eckel realized the importance of types and how a powerful type system could allow easy implementation of the domain model. As Strachey said, the first rule of programming is ‘figure out what you want to say before you figure out how to say it.’

    However, a type system is merely an abstraction and all abstractions simplify and give up some level-of-detail. Given a complex enough model, the type system will be too simple to specify a type contract in the language without a kludge workaround (hack). This goes for any language. Ricky Clarkson tried communicating to you exactly that: “About knowing what you are doing - it is possible to know what you are doing but be incapable of expressing what you’re doing to the compiler.” A type system provides confidence but it does NOT!!!! provide certainty.

    The statement “there are never times when you say ‘I know what I am doing’, when you don’t really know and you don’t want someone (or something) to tell you so” is true. However, if you paid close attention to what your readers were saying rather than trying to trump them with your genius, you would realize that your true statement ignores the fact that type systems DO NOT!!!! provide certainty.

  10. Tony Morris Says:

    At no point did I intend to “insult” Zwitserloot. I merely made a statement that I can support with lots of evidence. That is to say, I don’t wish to pursue Zwitserloot’s point because it is wrong and would require effort on my part. If I were to pursue every single statement that was wrong, I wouldn’t get much time for anything else, and I do not apologise for wanting to do other things.

    I merely noted what I see as common of Zwitserloot in dialogue with Kirit who appealed to Zwitserloot’s statements. I hope Kirit accepts that I do not wish to pursue Zwitserloot’s false position. It was an appeal to not have to expend effort on mediocrity more than anything. I applaud Zwitserloot for making mistakes and hope he keeps making them, as I do for myself.

    Nevertheless, you have made a crucial mistake and instead of recognising it, accused me of making a mistake. Isn’t life a fun game?

    Yes, type systems DO provide certainty. They are more commonly referred to as Theorem Provers. I will refer you to some if you’re interested.

  11. John "Z-Bo" Zabroski Says:

    I did not make a crucial mistake.

    I will say it again: Type systems provide confidence, not certainty. Certainty means 100% coverage. So far, you have admitted that it’s a good feeling to have someone (or something) tell you, ‘You are programming correctly’ when that is the case. Now, the next step is for you to realize that this is a false sense of security. The moment you have a problem domain model that cannot be guarded by your language’s type system, you will need a kludge workaround / hack.

    CONFIRM / DENY?

    I don’t need references to theorem provers. Again, please be polite. If I didn’t know what a theorem prover was, then I would search Google to find out. Moreover, I wouldn’t be reading a blog with a lambda insignia or citing Christopher Strachey if a theorem prover was a foreign concept to me. You could do well by considering your readers your peers.

    Zwitserloot is correct. I think you might be unfairly associating Zwitserloot with Kirit, as though they are the same person. Kirit’s comments were strange and he seemed to completely misread Zwitserloot.

    I.e.,

    This comment –&gt; As Reinier points out, I suspect you’ve mis-identified the aspects of a language that make it “industrial”.

  12. Reinier Zwitserloot Says:

    Now I’m just confused.

    From what I can see, Kirit’s comment (#7) isn’t a reply to me - it’s a reply to the OP with examples that are more appealing to the die-hard C/C#/Java/C++ programmer. As a complete aside in this rewrite of OP’s main thesis, he makes a note that my argument about what is meant when people call java ‘industrial’.

    Then, Tony accuses me of declaring that type systems are unimportant (#8). This baffles me. I’m not even talking about this in the one comment I made prior to this one, and in other writings of mine on the web, I am always in favour of complex and very expressive type systems. e.g. I favour Boo over Python, I’ve always expressed positive opinions over java generics (they certainly could be better but it’s a big improvement over not having them), and I want more expressiveness, such as for example a type modifier that implies it can never hold a null value.

    Then Tony posts again mentioning that he doesn’t want to, but could, trash my ‘point’ (#10). I wonder which point he is referring to, I made a couple, and it’s not clear from context. Tony also implies that Kirit is trying to prove me wrong, which I again don’t get - Kirit’s post agrees with an aside of mine, and reframes the OP’s argument in the link. How this is a reply to my comment?

    If it helps, when I post, I post under my name (Reinier Zwitserloot), and no other.

Leave a Reply