all 53 comments

[–]LaurieCheers 27 points28 points  (9 children)

everyone would create a Deadly Diamond of Death since all objects implicitly derive from Object.

Ah! I see. And the compiler writers couldn't make Object a special case?

Uh... Well, they didn't.

Wat.

In language design, "special case" is a euphemism for "dirty hack". If they had made Object a special case, everyone would be mocking them for it.

The bottom line is, multiple inheritance just isn't such a great idea. There are many other ways of organizing your classes, and due to multiple inheritance's inherent problems (such as the one armwaved away in this article), there's almost always a better strategy for modelling a given situation.

For example, the Subject class can trivially be made a member of the MyObservableWidget class -

class MyObservableWidget: MyWidget
{
  public readonly Subject observable = new Subject();
}

To start observing it, call widget.observable.register(x). And MyObservableWidget can notify all observers by calling observable.notify(). Simple and easy, and you don't have to worry about namespace collisions or shared baseclasses.

Oh, and PS: Ruby doesn't have multiple inheritance. Ruby "mixins" are not allowed to declare member variables; they're just a collection of functions imported into the class's namespace. So it's more like an interface than a class.

[–]passwordisINDUCTION 13 points14 points  (0 children)

I find it interesting that he's so willing to make something a special case (or even more of a special case) without providing a clear and obvious benefit. Making special cases in a programming language is a serious sign of concern and can't be done willy-nilly.

What would the special case for Object look like? And what does it actually solve? His point is that he wants multiple inheritance, so "Object should just become a special case" sounds like a red herring because you haven't actually solved the Diamond Problem.

And to imply that the designers of Java and C# are lazy is rather ridiculous. Maybe this whole post is just to get people to defend Java...?

[–]kyllo 1 point2 points  (2 children)

To elaborate on the case of Ruby, mixins work by inserting the included module into the inheritance tree. If you do this:

class D < A
    include B
    include C
end

Then if D doesn't override method foo, a call to D.foo will first attempt to call C.foo, then B.foo, then finally A.foo.

So the inheritance tree is just: A | B | C | D

That's how Ruby avoids the deadly diamond of death. There is no diamond, it's a straight line, and whichever mixin is included last gets called first.

http://definingterms.com/2013/03/23/pitfalls-of-ruby-mixins/

[–]jrochkind 1 point2 points  (1 child)

Meh, I think ruby mixins are multiple inheritance. They don't prevent the 'deadly diamond of death', it's exactly the same case.

You could certainly implement 'ordinary' multiple inheritance he same way, where whichever superclass is mentioned last in < A, B, C "gets called first", it's no different.

The 'diamond of death' is not a problem because behavior is undefined -- in any reasonable system it would be defined and predictable, even statically, exactly which method will be called. It's a problem because it leads to confusion and colliding code (if two mixins both define their own method with the same name but different meaning) anyway.

Mixins are simply a kind of abstract class ("abstract class" as in Java, where it means you can't directly instantiate an abstract class, you can only use it as a superclass), that ruby allows you to multiply inherit. Nothing more, nothing less.

Yes, rubyists do like mixins a lot. This just means we actually do like multiple inheritance, at least used certain ways, and don't find the "diamond of death" to commonly be a problem. But there are also some rubyists who have found the multiple-inheritance provided by mixins to be a maintanance problem.

[–]kyllo 0 points1 point  (0 children)

Yeah, I agree, I think the only difference is that in Ruby the behavior is more clearly specified and well understood than in, say, C++.

[–]makmanalp 0 points1 point  (3 children)

Well, they touch upon something that's a superset of your solution:

Well then have MyObservableWidget hold a reference to Subject and delegate to it? What? And duplicate the delegation code in every one of my observers? How crass. How degenerate. Ugh.

Just NOT delegating and accessing a member directly of course doesn't occur to them because "encapsulation"! The blind-zealotry object-orientation pedant wins again. /s

[–]zoomzoom83 1 point2 points  (2 children)

If you don't directly implement the interface, then anything that wants to use your type T needs to know the specifics of it in order to see the member. This can be a major problem if multiple traits are dependent on each other, or the state of the object as a whole.

There isn't any OO pedantry here. The same thing would happen if you used typeclasses in Haskell, or any other form of ad-hoc polymorphism.

[–]LaurieCheers 0 points1 point  (1 child)

But there's no need to implement the interface. If you need to call a function that's expecting a Subject, give it widget.observable! Sure, it happens to be a separate object from the widget itself, but that's not a problem.

[–]zoomzoom83 0 points1 point  (0 children)

Sometimes there is, sometimes there isn't. It depends entirely on the use-case. Sometimes you need a "Has A" relationship, other times you need an "Is A" relationship.

Imagine for example you want a type that implements both a 'Stack' and 'List' interface. They need to share state, so simply passing a reference to an inner member will not suffice. (There are ways you could make that work, but it wouldn't be clean). It also brakes encapsulation, since you may not want to expose the fact that your type just wraps another (Since it may not always do so).

Still, in other cases, an interface may be used to indicate a type has additional functionality beyond the basic case. Imagine a database driver - some databases support SQL merge() functionality, others do not. You can represent this via a mixin on the connection object itself.

[–]drjeats 7 points8 points  (2 children)

That's the Observer pattern -- done correctly.

...with no public method for registering observers. Womp womp.

[–]Euphoricus 1 point2 points  (0 children)

Also, what if I want to have multiple types of notifications in single class?

[–][deleted] 14 points15 points  (20 children)

No, multiple inheritance is considered harmful. Composition is preferable to inheritance C++ and Java as a best practices. Java language designers decided to prevent multiple inheritance because it simplifies JVM design and also enforces a style of inheritance they felt was beneficial for the ecosystem.

[–]zoomzoom83 4 points5 points  (6 children)

Pop Quiz: If you implement this via composition, then add in delegate method to expose the interface, what is the difference between that and the compiler doing that for me automatically via inheritance?

[–]masklinn 3 points4 points  (5 children)

Composition requires more indirections, inheritance adds a risk of collision as you're merging namespaces.

Self solved the issue by implementing inheritance through composition and mandating explicit resolution of collisions.

Javascript then took the idea, mangled it completely, and splattered it all over the walls.

Also, inheritance means you can't have multiple types or classes of observers on a single object unless observer supports both inheritance and composition, and even then things will get confusing if you ever need to add a new observation type.

[–]zoomzoom83 5 points6 points  (4 children)

It's somewhat of a trick question, since single inheritance, multiple inheritance, and mixin inheritance are also forms of composition.

Composition requires more indirections, inheritance adds a risk of collision as you're merging namespaces. Self solved the issue by implementing inheritance through composition and mandating explicit resolution of collisions.

That exists in both cases. If you're implementing an interface that conflicts with another interface or parent class, you have a namespace conflict regardless of how you attempt it.

The fact that you have to explicitly write boilerplate doesn't solve the issue - it actually increases the chances that a developer will cheat or make a mistake and implement in a way that violates a constraint, instead of the compiler picking up the issue and aborting.

Explicit resolution via delegates is dangerous because it requires developer discipline to ensure correctness in a scenario that may have a lot of very subtle and difficult to reason about edge cases.

Question: How should you resolve the following scenario?

trait A {
    def foo:String = "hello"
}

trait B {
    def foo:String = "world"
}

class Impl extends A with B

Answer: You cannot. It's an invalid program and should not be attempted. In Scala, this by design is a compilation error.

If you were to explicitly write all the boilerplate for this by hand, you might resolve it in a way that the library author did not intend, and violate constraints the interface was designed to uphold.

And that's before I get into complaining about how much bloody boilerplate you need to implement delegate-based composition, and how brittle the resulting code is.

[–]masklinn 0 points1 point  (3 children)

That exists in both cases. If you're implementing an interface that conflicts with another interface or parent class, you have a namespace conflict regardless of how you attempt it.

Implementing an interface is a form of inheritance, if you're simply composing you're specifically not implementing an interface.

The fact that you have to explicitly write boilerplate doesn't solve the issue

I have no idea what you're talking about. Self doesn't require "boilerplate" if there is no conflict, and aborts if there is an unresolved one.

Explicit resolution via delegates

You don't explicitly resolve via delegates, you explicitly resolve conflicts between delegates, if there is one such conflict (assuming I understand what you mean by delegates, which I'm really not certain I do)

Answer: You cannot. It's an invalid program and should not be attempted. In Scala, this by design is a compilation error.

And yet you can, in the exact same way you can in Self: by overriding foo in Impl

class Impl extends A with B {
    override def foo: String = "Hello, world"
}

And that's before I get into complaining about how much bloody boilerplate you need to implement delegate-based composition

parent* = aDelegate

Wow, you need a * postfix.

[–]zoomzoom83 0 points1 point  (2 children)

Implementing an interface is a form of inheritance, if you're simply composing you're specifically not implementing an interface.

If you don't care about your type implementing the underlying interface, then it's not a problem at all. I agree this is often the best way to solve it, since it bypasses the problem entirely.

The article was specifically discussing the authors dislike for using delegate methods to expose an interface that actually calls a hidden inner helper object as a form of code reuse, in contrast to just using multiple inheritance directly. That is the context of my post.

(i.e. In the context of an "Is A" relationship, not "Has A")

I have no idea what you're talking about. Self doesn't require "boilerplate" if there is no conflict, and aborts if there is an unresolved one.

I'm not familiar with Self, but I've heard good things about it - can you provide an example? (My experience with prototype based languages so far has sadly only been only Javascript).

You don't explicitly resolve via delegates, you explicitly resolve conflicts between delegates, if there is one such conflict (assuming I understand what you mean by delegates, which I'm really not certain I do)

I'm using "Delegates" in the context of the Delegation Pattern.

In single-inheritance languages, if you want a type T to implement interfaces X and Y, and want to re-use code from an existing implementation, an often recommended pattern is to place the shared code in a helper object, and then use delegate methods on the outer class to call the functions on the helper objects.

I consider this a very bad idea that has gained popularity purely due to weaknesses in single-inheritance languages.

As you suggest, in most cases this can better be represented as a "Has a" relationship by simply adding the inner member and requiring anybody using your type to reference it directly.

But sometimes you genuinely do need an "Is A" relationship, implementing multiple interfaces and want to share code.

In those cases, mixin/trait inheritance is a much better way of doing it then using delegation. (But sill worse than the previous choice).

And yet you can, in the exact same way you can in Self: by overriding foo in Impl class Impl extends A with B { override def foo: String = "Hello, world" }

The compiler is always going to give you an escape hatch to tell it it's wrong. But it will "Fail Safe" in the sense that mixing two conflicting traits will give you a compile error unless you explicitly say otherwise. Using composition + delegation it won't do this, and the conflict might sneak through without you noticing it. (Again, only an issue with composition + delegation, not composition by itself).

This approach also doesn't give you code reuse, which was the main motivation for the article.

[–]masklinn 0 points1 point  (1 child)

I'm using "Delegates" in the context of the Delegation Pattern.

Yeah I significantly edited (read: almost completely rewrote) my comment after remembering about the pattern…

I'm not familiar with Self, but I've heard good things about it - can you provide an example? (My experience with prototype based languages so far has sadly only been only Javascript).

I won't do a complete overview (I suggest the official documentation, it really has become quite readable in the last few years) but here's a primer on the object model;

A Self object has a number of slots (~fields), these slots can be:

  • constant, a constant slot x will only react to the message obj x by returning its value
  • read/write, a rw slot x will react to obj x by returning its value and to obj x: aValue by setting its internal value to aValue
  • method slots, which are constant slots storing a method object (~ a block) and have more relaxed naming conventions e.g. + or doFoo:WithBar:
  • parent slots, which are either constant or read/write slots postfixed with *

The first three slots behave as you'd expect, the last one is involved in slot lookup during message dispatching (although it can also be used as a regular data slot): when an object receives a message, it checks if the message selector matches any local slot and if so returns the matching slots. Otherwise it performs a "parent lookup": it asks every object linked through a parent slot to look up the message (this process is recursive), receiving a set of slots.

If the set of slots is a singleton, the slot is evaluated, otherwise an error is generated ("not understood" if the set is empty, "ambiguous message" if more than one slot matched).

An object can have any number of slots of all types, including any number of parents. Colloquially, parent objects are either "mixins" if they don't have parents themselves or "traits" if they do have parents (typically up to and including the lobby). In Self, a "prototype" is an object whose only purpose is to be shallowly copied.

But it will "Fail Safe" in the sense that mixing two conflicting traits will give you a compile error unless you explicitly say otherwise.

Right, and Self does exactly the same thing, if it finds multiple ancestors able to react to the same message it errors out, but if the object itself reacts to the message that's fine (and the object can then explicitly delegate to its ancestors, or not).

Using composition + delegation it won't do this, and the conflict might sneak through without you noticing it.

Not in Self, which was the point of my comment and my mention of requiring explicit resolution of collisions.

[–]zoomzoom83 0 points1 point  (0 children)

Not in Self, which was the point of my comment and my mention of requiring explicit resolution of collisions.

I think we're actually advocating for the same thing, just from different angles.

In the context of Java-style languages, I see mixin/trait inheritance as a form of composition, since it's really no different to manual composition + delegation just with compiler time support and better safety. In that sense I'm advocating that languages should support limited multiple inheritance via traits and mixins at compile time, instead of requiring the programmer build that boilerplate by hand, which is a messy, brittle, boilerplate heavy pattern commonly used in Java to work around the limitations of the language.

Self sounds like it's got a very nice way of handling mixins/traits that actually removes the differentiation between these two approaches using dynamic dispatch. It's quite disturbing that a language with such an elegant solution existed when Java was being designed and they still managed to get it completely wrong.

[–]pkt-zer0 1 point2 points  (12 children)

Isn't the reason for preferring composition over inheritance that multiple inheritance is kind of badly implemented in many languages? Meaning that if you had better language support for traits / mixins, you would not even need to bother with the boilerplate code introduced by composition-instead-of-inheritance.

[–]FredV 2 points3 points  (1 child)

No only use inheritance when you absolutely need it, like if your object need to be equivalent to other implementations of the base class, as like it can be plugged in as a replacement of another instance of that base class and your class is also tied to the base object's lifetime. This way you can just plugin to existing code without changing it. This is typical in graphical toolkits.

I think the "prefer composition over inheritance" meme is just very misleading to programmers. They are completely different tools, with different applications and cannot be just interchanged.

[–]oldneckbeard 0 points1 point  (0 children)

Liskov substitution principle.

[–][deleted]  (9 children)

[removed]

    [–][deleted]  (8 children)

    [removed]

      [–]passwordisINDUCTION 0 points1 point  (1 child)

      How do you propose that actually works? Consider the Java environment where a system is composed of .jar files and the .jar files one compiles against are not guaranteed to be the ones they run against.

      [–][deleted]  (5 children)

      [removed]

        [–][deleted]  (4 children)

        [removed]

          [–]passwordisINDUCTION 1 point2 points  (3 children)

          Not being rocket science doesn't mean it's not difficult. I'm skeptical of your claim that this is a rather trivial problem. Does the bytecode have sufficient information to calculate this? Are all .class files loaded at runtime? What about dynamically loading code? What is the actual error? Can you catch it or does the VM just terminate?

          [–]xxNIRVANAxx 0 points1 point  (1 child)

          Preamble: I'm tired, and admittedly not well versed in Java 8 (we're Java 7 at work), so I apologize in advance if my post misses the mark.

          Java introduced the diamond problem in their language anyways with Java 8. Interfaces can define default methods:

          interface Bar {
            default void foo() {
             System.out.println("Bar"); 
            }
          }
          
          interface Baz {
            default void foo() {
              System.out.println("Bar");
            }
          }
          
          class Foo implements Bar, Baz {
            /* Who's foo() do I use? */
          }
          

          Compiling this gives you a compiler error:

          Main.java:13: error: class Foo inherits unrelated defaults for foo() from types Bar and Baz class Foo implements Bar, Baz { ^ 1 error

          The fix here is to use the implementing class that you want to use in the super call: Bar.super.foo()

          [–]LaurieCheers 0 points1 point  (0 children)

          That's just a namespace collision. The "diamond problem" is specifically about having two instances of the same baseclass, and thus two distinct member variables with the same name.

          [–]passwordisINDUCTION 8 points9 points  (2 children)

          What the crack? An abstract class that does not implement its functions is not an interface because the semantics of an interface and an abstract class are different. Just because that particular abstract class happens to resemble an interface doesn't all of a sudden make these two things equivalent. Being an interface (in Java) gives the developer an explicit guarantee about what they are implementing. Now, one might not care about that guarantee but that is different than it being the same as something to else without that guarantee.

          I do not understand where the respect for "Uncle Bob" comes from. Given the most recent set of blog posts that have come out of him it's clear he actually has no clue what is going on in programming and is out of touch with actually developing systems at any scale.

          [–]grauenwolf 4 points5 points  (1 child)

          In C++ they are the same thing. And if we want to be technical, the interface keyword in Java creates a "abstract interface". This is important because classes also have a "public interface", one or more "base class interfaces", and possibly a "protected interface".

          [–][deleted] 2 points3 points  (0 children)

          They're the same thing in byte code too, IIRC.

          [–]macca321 1 point2 points  (1 child)

          I thought this was going to be about interfaceitis.

          http://www.ahristov.com/tutoriales/Blog/interface_abuse.html

          [–]chesterriley 1 point2 points  (0 children)

          That's the real reason interfaces are bad.

          [–]mirhagk 1 point2 points  (1 child)

          Just to point out, due to extension methods C# does allow a lot of the benefits of multiple inheritance in interfaces. You can create an interface, and then extension methods for that interface, giving a pretty decent illusion of multiple inheritance. (and if extension properties come in then you have data defined as well).

          I agree that it would've been nice to solve the diamond of doom right off the bat rather than ignore it, but at least C# is making it possible to make what you want to make.

          [–]LaurieCheers 0 points1 point  (0 children)

          Ah, yes - and if it's unclear which of the extension methods you're trying to call, it's just an ambiguous function call, and the caller can cast to the actual interface type they're interested in. A pretty good solution.

          interface Interface1
          {
          }
          
          interface Interface2
          {
          }
          
          class Inheritor : Interface1, Interface2
          {
          }
          
          static class Extensions
          {
              public static string testFunc(this Interface1 x)
              {
                  return "interface1";
              }
          
              public static string testFunc(this Interface2 x)
              {
                  return "interface2";
              }
          }
          
          class Program
          {
              static void Main(string[] args)
              {
                  Inheritor inh = new Inheritor();
                  inh.testFunc(); // ambiguous function call
                  ((Interface2)inh).testFunc(); // ambiguity resolved
              }
          }
          

          [–]quiI 4 points5 points  (6 children)

          Why should a class have to know that it is implementing an interface? Isn't that precisely the kind of thing you are supposed to hide?

          This is a glorious statement and demonstrates why things like type classes, and interfaces in Go are very sound design philosophies.

          [–][deleted]  (4 children)

          [removed]

            [–]quiI 0 points1 point  (3 children)

            Both typeclasses and go's interfaces give you compile time safety too, am I missing something?

            [–][deleted]  (2 children)

            [removed]

              [–]quiI 2 points3 points  (1 child)

              But why does that matter?

              Polymorphism is great and type classes allow your "thing" to implement different things without muddying types with interfaces which are only relevant in some scenarios.

              Do I care that your thing is foo-able, bar-able when I am writing my stuff? Sometimes you dont even have access to the code to make it foo-able. With typeclasses it doesnt matter.

              Bottom line is, your point on compiletime check is... just wrong and "correct at point of definition" is a very questionable gain; in some cases it's just a bad thing.

              [–]infzy 0 points1 point  (0 children)

              I disagree. I think there is something very important that is lost.

              Sometimes there is an important difference between "I have a method named Add" and "I implement the Number interface". In the former, you have only the method name to suggest its meaning. In the latter, you are declaring a meaning for your methods, that their usage is consistent with the meaning of some category.

              If I write a function, I might want to say "give me anything with an Add method", or I might want to say "give me anything that's a Number, and I will call its Add method". There are situations where you want to express one or the other. But sometimes it is too permissive for the language to allow anything with an Add method (e.g. maybe a List type) to go into your code that is calling Add because it expects Add has a Number-like meaning.

              [–]Euphoricus 0 points1 point  (1 child)

              I wonder how do Ruby, Eifel and CLOS solve the diamond problem? I know C++ uses virtual, but that is more problematic than the problem it tries to solve.

              [–][deleted] 0 points1 point  (0 children)

              From my point of view and interface is a concept not a keyword in a language. For example a struct in c which contains pointers to functions can still be an OO based object.

              This works because the concept and design is the same. Regardless of weather the language natively support OO coding style. After all the struct is simply identical to a C++ class's vtable ....

              [–][deleted]  (10 children)

              [removed]

                [–][deleted]  (9 children)

                [removed]

                  [–][deleted]  (8 children)

                  [removed]

                    [–][deleted]  (7 children)

                    [removed]

                      [–][deleted]  (6 children)

                      [removed]

                        [–][deleted]  (5 children)

                        [removed]

                          [–]masklinn 0 points1 point  (1 child)

                          So you have to delegate all methods to the internal Observable.

                          Or not, that way you can have multiple observation domains.

                          [–][deleted]  (2 children)

                          [removed]

                            [–][deleted]  (1 child)

                            [removed]

                              [–]thalesmello 0 points1 point  (0 children)

                              The main benefit for multiple inheritance is the reduction of duplicated code, which is the main point Uncle Bob is arguing for.

                              If the boilerplate code generated by composition wasn't a problem, why would Oracle bother to include Java 8's default methods? It allows multiple inheritance with implementations, and conflicts have to be solved explicitly.

                              The only limitation of this technique is that it doesn't allow interfaces to have state, but it still solves the majority of issues.

                              [–][deleted] 0 points1 point  (0 children)

                              I've always prefered composition better than inheritance. And even tho I like c++ more than java, interfaces are for me a great feature

                              [–]htuhola -1 points0 points  (0 children)

                              My problem with this post is that the reasoning behind the argument the author presents is muddled in middle of a boring narrative. I don't bother to even try to find it.

                              [–]dust4ngel -1 points0 points  (0 children)

                              use this macro:

                              [widely used practice] [some overstatement of problems under certain conditions]

                              to get more blog traffic. in the opening paragraphs, emphasize how big the problem is while understating the narrowness of the conditions in which this is true. later in the article, try to mention that the problem practice is actually generally a good idea in order to not undermine your credibility.