Interfaces with default implementations are better than classes for a number of reasons, including the diamond problem, and that as systems grow large they almost never fit cleanly into an inheritance tree. Fat pointers with two words, one for an interface vtable and one for the object, work really well.
In your view, how is "interfaces with default implementations" different from "inheritance for code reuse"? At a minimum it looks like a virtual function with a default implementation in the base class. If a derived class doesn't override it, isn't that code reuse?
> In your view, how is "interfaces with default implementations" different from "inheritance for code reuse"?
The latter is a more general classification?
A type which inherits a default method implementation from an interface is an example of "inheritance for code reuse".
A type which inherits a method implementation from a parent class under classical OOP is also an example of "inheritance for code reuse".
However, I argue that "interface inheritance with default implementations" is superior to "classical OOP" because it avoids tight coupling with memory layout, problems with implementing multiple inheritance in classical OOP, etc.
I believe that both Go and Rust use two-word fat pointers for interface types. I was actually thinking of Rust traits first and foremost; in Rust, traits can supply default method implementations which invoke other methods defined in the same trait.
While I take your point about introducing assumptions, I'm reluctant to give up the convenience of default implementations; they seem to present fewer problems than classically inherited methods because shallow hierarchies are more common with interfaces than with classical inheritance, and because default interface methods cannot directly access member variables because they do not know about object layout — unlike methods inherited from a parent class under classical inheritance.
> Interfaces with default implementations are better than classes for a number of reasons
it's literally the same thing in practice
> including the diamond problem,
the diamond problem has only ever been a "problem" in OOP textbooks, in years and years of working on OO system I have never saw it be a problem in practice
The plan seems so complex that it makes complete sense to me why languages would avoid multiple class inheritance (where each class is afforded direct access to member variables).
In contrast:
• With single inheritance, you don't have to resolve different member variable layouts because there is only one.
• With interface inheritance (with or without default methods), interface methods don't get to access struct members directly and know nothing about object memory layout.
> Interfaces with default implementations are better than classes for a number of reasons, including the diamond problem [...]
"The diamond problem" only occurs with multiple inheritance. At a (wild-ass) guess, only a minority of "traditional" (=inheritance-based) OOP languages have that; certainly not all of them. So as far as "the diamond problem" is concerned, interfaces with default implementations are no better than single-inheritance classes.
Inheritance is just a tool to mostly avoid. It tightly couples different class implementations. So will make later refactors error-prone and tend to scatter logic. It's misunderstanding OO as messaging between objects.
Using composition or lay out data differently may yield designs with more desirable properties. There's no one answer and it depends.