1. 35
    1. 10

      These posts are the height of observational comedy. Please don’t stop.

      I noticed, while implementing a flavor of E, that the lack of inheritance reduces the amount of work required to do dispatch inside the runtime. Further, there was no need to consider superclass information when laying out each type of object; instead, each object literal gets its own individual layout. If a programmer desires it, delegation can substitute for inheritance, and delegation is just a special form of composition. If a programmer desires more, then cooperative inheritance between a child object and a parent object is possible, where the objects are mutually recursive.

      1. 7

        Who would have thought that removing features would make the language implementor’s job easier? Remarkable. It’s a shame some people have to use languages instead of implementing them.

        Sarcasm aside, in my experience Go’s flavor of kinda-sorta-inheritance seemed to work ok for a while, until I ran into something where the relations between types were a bit more complex, and then things got ugly real fast. That code would have been trivial in a language with real inheritance.

        1. 3

          Languages are created by geeks and nerds, two classes of people who revel in the complex.

          Inheritance is generally an anti feature, especially on larger codebases. My dream oop language is a smalltalk using the traditional filesystem for code, immutability as core, and no inheritance.

          1. 3

            Inheritance is generally an anti feature, especially on larger codebases

            I disagree, as someone who’s worked on many larger codebases like WebKit, Chrome, Google Sites, Safari, Mail.app and the JDK 1.2 class library; and delved into the innards of AppKit and Foundation. These all use inheritance, trust me.

            I know there are schools of programming that discourage inheritance, but “generally” is overstating it.

            1. 1

              Do you think those codebases were easier to use and understand and debug because of inheritance? My general experience has found the larger the codebase the worse it was affected by inheritance ideals.

          2. 2

            Check out Erlang. Process is an object in such case, and messages are method calls.

    2. 7

      Suggest only the plt tag, since tags are filtered via intersection and not union. :)

      1. 12

        Tags should be partially ordered in a lattice, with “sub-tags” being substitutable for any “super-tags” higher up in the lattice.

        1. 4

          If we’re brainstorming, I’d suggest not ordering them initially and instead allowing people to use a blang to query them.

        2. 2

          I see what you did there.

    3. 6

      In self prototype is a memory optimisation hack. You don’t need prototypes or inheritance for that matter in javascript if you just copy, clone and merge dictionaries. Languages that support generic maps don’t need a special class syntax because you can always roll your own vtable. In static languages because there is no generic clone, copy and merging of “structs” you need a special syntax for inheritance.

    4. 7

      I find inheritance very useful for structuring my own code. However where it seems to falls down is when you make a big framework class and let other people inherit from it. Code that uses this mechanism across module boundaries doesn’t seem to grow gracefully.

      Also, I find the most natural uses of inheritance have 1 to 3 virtual/overridden methods and maybe 5-10 overall. If you see 5 overridden methods and 20-30 overall, then IMO that’s a design smell.

      I think the Go designers said that “the power of of an interface is inversely proportional to the number of methods it has”. I find that to be true of inherited methods.

      You can use inheritance like interfaces in small- / medium- codebases, where you are doing global refactoring. (It’s definitely true that some large codebases and ecosystems have horrible inheritance hierarchies that can never be changed, and that’s something you really want to avoid IMO.)

      Go interfaces are nice but they also have a surprisingly tricky runtime implementation. Inheritance is simpler (and yes performs better!)

      1. 1

        One thing I realized with a little more thinking is that I no longer seem to use inheritance hierarchies more than 1 level deep. When you have something 2 levels deep it seems better to factor it into composition and inheritance, or composition only. This feels like “interfaces” a bit, since interfaces don’t really have a hierarchy.

        Also I’d say that implementation inheritance probably only happens in 20% or less of the code. I don’t think the whole program should be architected around inheritance… It’s just one tool, and composition is the “default”.

    5. 4

      I feel like inheritance is the new goto, in that all the people using it in a crazy way are spoiling the sane, practical uses. For instance, where I work we’re using D, which gives us lots of practical ways to reuse code that aren’t inheritance (templates, mixins etc.) and as a result we only tend to use inheritance in very flat hierarchies for structuring dependencies between areas of the codebase via dependency inversion, resolving dependency cycles, and decoupling semantics from implementation. Basically, most classes inherit from one interface. We’re finding it very useful for that, but I’m worried that people who don’t bother too much with how to structure large codebases are taking the wrong lesson of “always use composition” from C++‘s crazy inheritance abuses. Like how a retry loop may be genuinely easier with goto, but goto has gotten such a bad reputation that everyone is doing the while-continue-break “Gentleman’s Goto” antipattern instead.

    6. 3

      a reference count scheme, an idea borrowed from Weizenbaum (1962)

      Is that Joseph Weizenbaum, of Eliza fame? If he introduced reference counting, then I’d say that may be his greater, and vastly under-appreciated, contribution to computer science.

      1. 8

        “Why do you say it’s his greater, and vastly under-appreciated, contribution to computer science?”

        (sorry couldn’t resist)

      2. 3

        Reference counting is usually credited to Collins; he published it in 1960. https://en.m.wikipedia.org/wiki/George_E._Collins

        1. 3

          Interestingly, the credit to Weizenbaum comes from the original HOPL I (1978) paper on the history of Simula by several of Simula’s designers, which this blog post follows. The paper says:

          we implemented a reference count scheme, an idea borrowed from Weizenbaum (1962)

          So it sounds like they got it from Weizenbaum. The Weizenbaum (1962) paper they cite does in turn credit Collins (1960) though. I believe more broadly Weizenbaum’s role in the history here is that he wrote the first implementation that got much distribution to other researchers and influenced other languages. Collins was at IBM, and as far as I can tell his reference counting implementation was for a pretty specialized project (an attempt to implement Tarski’s decision procedure for the reals) and was never available outside of IBM. While Weizenbaum programmed a reference counter for the SLIP language, which is what Eliza is written in, and was more broadly known.

    7. 3

      Their followup paragraph provides additional important context:

      It was evident that when prefixing was introduced, it could be extended to multiple pre- fixing, establishing hierarchies of process classes. (In the example, “car” would be a sub- class of “link,” “truck” and “bus” subclasses of “car.”) It was also evident that this “concatenation” of a sequence of prefixes with a main part could be applied to the action parts of processes as well. Usually a new idea was subjected to rather violent attacks in order to test its strength. The prefix idea was the only exception. We immediately realized that we now had the necessary foundation for a completely new language approach, and in the days which followed the discovery we decided that:

      1. We would design a new general programming language, in terms of which an im- proved SIMULA I could be expressed.
      2. The basic concept should be classes of objects.
      3. The prefix feature, and thus the subclass concept, should be a part of the language.
      4. Direct, qualified references should be introduced.

      So while they originally came up with it as a performance hack, they realized it had broader applications. Kinda like how uranium was originally used as a yellow die before we repurposed it for nukes.

    8. 3

      This is an interesting tidbit, but it’s as relevant as pointing out C’s signed integer overflow undefined behavior is due to ones’ complement architectures. That’s correct, also we probably shouldn’t care about ones’ complement architectures any more, but that doesn’t mean signed integer overflow should be defined. Whatever its origin, it now serves different purposes.

    9. 3

      We need inheritance tax then.

    10. 2

      This is interesting, because if I read it right it explains all of the pain I had in the 90s over C++Builder’s collections. You had to create the list item type derived from some base list item, add your items, then you could add them to the collection. Obviously not a generic solution like would come along in standardization.

    11. 1

      I think I’m missing part of the picture. I get how using intrusive lists is good for performance, but is there something that prevented them from just copy-pasting the linked list code into every class?

      Of course, you wouldn’t want to do that. But the reason you wouldn’t want to do that seems like a standard story of what inheritance buys you: code reuse.

      1. 1

        In modern C++, I can use std::list<T> to make a linked list of objects of type T. I get the same efficient memory layout as with intrusive lists. But I don’t have to modify the type T to inherit from a list_node type. std::list works with any type, because it uses composition, not inheritance.

        In early C++, before templates were added, the only way to get this kind of performance was using inheritance and intrusive lists. Using inheritance for this purpose degrades code reuse. Composition is better.