Yes, we got it by now, terrible implementations of inheritance and terrible usages of said terrible implementations are terrible.
Inheritance is just one tool in the toolbox. Use it wisely where it makes sense.
For a point of comparison, I’m already not very OOP inclined, but the number of times I’ve genuinely wanted to make use of inheritance in a 15 year period I can probably count on two hands. Almost always I would prefer a compile-time subtyping tool like ML functors.
… I can probably count on two hands.
So… fewer than 1024 times?
Yes, that is a correct statement, it is less than 1024 times.
Failure to understand the LSP is terrible. https://en.wikipedia.org/wiki/Liskov_substitution_principle
I think the point is that inheritance encourages LSP violations, at least in common implementations. It’s telling that in Smalltalk, the One True Object Oriented Language ™ (at least in Pharo, which is the Smalltalk dialect I have experience with), instance variables are always private and inaccessible to subclasses.
I submit that everything is terrible if you attempt to use it to solve all problems.
This means we’ll probably never run out of “X is terrible” programming missives.
That’s just the first reason this particular well will never run dry, the second is that people enjoy dropping a firecracker into the lake just to watch the ripples.
See also “XXX is DEAD” articles.
Yes, I agree that the phrasing like this is not very valuable. Instead I think an interesting way to put it is: Is inheritance a useful enough tool to be a first class citizen in a language? I claim that the answer is No. Inheritance is generally not very useful and can be emulated with a record and some closures when it is the right tool.
[Comment removed by author]
The description of typeclasses in the Haskell standard makes some sense, although it doesn’t address the well-known concerns about modularity.
But GHC’s implementation of typeclasses is irreparably broken, so I’d say that using them certainly adds complexities.
Haskell typeclass inheritance has exactly the same problems, no? You just see them less often because you don’t have a complex hierarchy of typeclasses that often.
Actually, a haskell typeclass doesn’t inherit but requires other typeclasses. A haskell typeclass is similar to an interface in other languages.
Yes and no. You do have implementation inheritance and subtyping (a monad instance is an applicative instance, and the two share implementation code). No?
Also, haskell doesn’t mix functions with data, meaning it doesn’t have something like a class from C++ or java.
Typeclasses do mix functions and data though? E.g. monoid contains the zero value (data) and some functions.
No need to be so condescending and rude. I know perfectly well what typeclasses are. When you talked about “typeclass inheritance” I assumed you meant typeclasses inheriting from each other, because that’s what that phrase usually means; if you’re talking about using typeclasses as a replacement for inheritance that’s a different thing and one that has its own set of problems.
So how is that not data by your definition? (I mean the zero value had better always be the same value, otherwise you’ll violate the monoid laws). I don’t think the article does even claim that the problem is mixing functions and data. And if anything I’d say functional languages are all about mixing functions and data.
I don’t see what difference it makes whether we have a datum called price or a function called get_price. The problem is exactly the same either way.
Thinking about it I guess I just don’t see the value the article is claiming. It makes no difference whether price is a datum or a function. The refactoring would have been exactly the same if it had been a function from the start. And explicit visibility markers (public/protected/private) are great, but irrelevant at this stage; I don’t get what the article is saying about inconsistent levels of abstraction at all. A Book has a price. Why not make it look like it has a price?
I’ve worked on a 400kloc java codebase and a wide range of scala codebases.
My experience is that traditional OO inheritance can add a lot of value when used appropriately. Often composition is better, but not always; often typeclasses are useful, but not always - replacing all inheritance with typeclasses makes some use cases substantially more complex.
What are the differences then? You don’t have compiler-enforced coherence in Scala which is a shame but I’ve literally never seen it actually make a difference in practice.
At least in the case of monoid, the zero value is not the zero value for a particular value of a Monoid, but rather the zero value for the instance of the Monoid. So it’s closer to a static member function (which most OO languages that I’ve seen do not include in their inheritance model.
One also does not inherit code AND data in type class ‘inheritance’. If I extend Foo in Java I get all the data Foo contains. If I have an instance of Foo in a type class, that says nothing about the data my instance must contain. I think.
And I don’t think @crocket is being very civil or helpful in this discussion.
Yeah - I was confused by the phrase “typeclass inheritance” which I took to mean @crocket was talking about typeclasses extending other typeclasses but that turns out to not be what they meant.
What was incorrect? If you’re unwilling or unable to pursue a discussion don’t just flatly say I’m wrong.