Earlier quoted context omitted.
It’s a misuse because (if you really are only using it for code reuse) it’s creating an essential relationship between different objects that is actually incidental. A list containing two different subclasses of your avoiding-copy-paste base class is now most likely a logical error.
how is that different from a list containing 2 implementations of an interface?
Inheritance was invented as a performance hack
41–50 of 268 posts
Re: Inheritance was invented as a performance hack
#42I spent way too long trying to figure out how giving your kids your lifetime's earnings would be a performance hack beyond, you know, giving them a head start, before realizing what the headline was talking about.
On a more serious note, thinly veiled threats by patriarchs to their children working in the family business regarding their eventual share in the business is something that probably happens routinely around the world, especially in areas like Europe, East Asia, South Asia, and South East Asia, with many multi-generational privately held family businesses.
Re: Inheritance was invented as a performance hack
#43Earlier quoted context omitted.
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?
Maybe he meant something similar to Go interfaces, but they are abstract and coupled loosely with implementations after the fact. That or abstract class/pure interface. There's no need for default implementations introducing assumptions in code.
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.
Re: Inheritance was invented as a performance hack
#44Fine. But obviously it caught on for other reasons, like code reuse and an intuitive mental model. The HN appetite for posts ragging on inheritance will never be sated.
"Catching on" doesn't mean "is the right way to do things". Code reuse is in fact a prime example of a misuse of inheritance
Re: Inheritance was invented as a performance hack
#45Fine. But obviously it caught on for other reasons, like code reuse and an intuitive mental model. The HN appetite for posts ragging on inheritance will never be sated.
"Catching on" doesn't mean "is the right way to do things". Code reuse is in fact a prime example of a misuse of inheritance
Specialization remains a very common design pattern that is incredibly useful and trivially and intuitively solved with inheritance. No other programming concept (HKT, ad hoc polymorphism, functional programming, etc...) comes close to its elegance.
Re: Inheritance was invented as a performance hack
#46Earlier quoted context omitted.
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?
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.
Re: Inheritance was invented as a performance hack
#47First, inheritance from an interface/trait is totally okay. The problem is class inheritance, meaning implementation inheritance.
There are two cases:
1) you inherit from a class and only add methods but don't overwrite anything. This is the good case, you can do that
2) you inherit from a class and overwrite one ore more of its methods. This is the bad case, no matter how you look at it. And here is why.
Look at the following classical simple Stack class:
class Stack(...) {
def push(element) returns nothing = ...
def pushAll(elementlist) returns nothing = ...
def pop() returns element = ...
}
Now imagine you want to keep a count of the number of elements, so you decide to extend the class: class CountingStack(...) extends Stack {
override def push(element) returns nothing =
count += 1; super.push(element)
override def pushAll(elementlist) returns nothing =
count += elementlist.size; super.pushAll(elementlist)
override def pop() returns element =
count -= 1; super.pop(element)
}
Looks good no? Can you spot the problem?If the Stack class implements pushAll as iterating over the elementlist and calling push for each element, then the CountingStack will count twice!
We could try to just not overwrite pushAll in CountingStack - but what if the Stack class is later changed to implement pushAll without calling push? Then CountingStack would now miss counts.
This problem is impossible to fix and it is a general problem - when overwriting a method, you can never be sure that semantics could break when the base class is changed.
So what is the solution here? Simple: use composition over inheritance.
class CountingStack(underlyingStack) {
def push(element) returns nothing =
count += 1; underlyingStack.push(element)
override def pushAll(elementlist) returns nothing =
count += elementlist.size; underlyingStack.pushAll(elementlist)
override def pop() returns element =
count -= 1; underlyingStack.pop(element)
}
Problem solved - no matter how Stack is implemented, our CountableStack will always be correct. The drawback (if you want to call it one) is that we need some common interface/trait that both Stack and CountableStack will implement.I hope that sheds some additional light on why (implementation) inheritance is often considered a bad practice.
Re: Inheritance was invented as a performance hack
#48Earlier quoted context omitted.
How is it a misuse? And what are the alternatives? To be clear, I do agree with your assertion, however I'm having a hard time actually putting "how/why" into words.
It’s a misuse because (if you really are only using it for code reuse) it’s creating an essential relationship between different objects that is actually incidental. A list containing two different subclasses of your avoiding-copy-paste base class is now most likely a logical error.
So don't allow those? Supporting inheritance doesn't mean you have to support heterogeneous lists.
Re: Inheritance was invented as a performance hack
#49After reading all the comments of many confused and curious, here is when inheritance is bad and why so _in the absence of any performance considerations_. First, inheritance from an interface/trait is totally okay. The problem is class inheritance, meaning implementation inheritance. There are two cases: 1) you inherit from a class and only add methods but don't overwrite anything. This is the good case, you can do…
Re: Inheritance was invented as a performance hack
#50Earlier quoted context omitted.
How is it a misuse? And what are the alternatives? To be clear, I do agree with your assertion, however I'm having a hard time actually putting "how/why" into words.
It’s a misuse because (if you really are only using it for code reuse) it’s creating an essential relationship between different objects that is actually incidental. A list containing two different subclasses of your avoiding-copy-paste base class is now most likely a logical error.