Live data from Hacker News

Inheritance Often Doesn't Make Sense

sicpers.info

51–60 of 255 posts

Re: Inheritance Often Doesn't Make Sense

#51
> Inheritance was never a problem: trying to use the same tree for three different concepts was the problem.

I wonder why the author describes Polymorphism[1], but doesn't mention the term in any way. At the same time he uses the word 'abstract', but in a different way than any OO/Smalltalk programmer would do. While he mentions the 'Smalltalk blue book', I somehow do not trust his expertise in the field.

[1]: https://en.wikipedia.org/wiki/Polymorphism_(computer_science...

Re: Inheritance Often Doesn't Make Sense

#52
post #20

> you cannot use a square everywhere you can use a rectangle (for example, you can’t give it a different width and height) Can someone come up with a better example here? Intuitively, I would say, "Yes, if you ask me for any rectangle, and you reject a square, you are wrong." If you say you can use any rectangle to do your thing, you should absolutely be able to also use a square. Why am I not convinced with the give…

The square vs rectangle example is a great one for showing how to not use types at all, IMO.

> As a type, this relationship is reversed: you can use a rectangle everywhere you can use a square (by having a rectangle with the same width and height), but you cannot use a square everywhere you can use a rectangle (for example, you can’t give it a different width and height).

Which is a good thing (yes, I'm a proponent of strong typing, like in Rust). If you want that square to be used in places where only a rectangle can be, you should either be explicitly required to cast it to a rectangle type or have a language that can define and handle contravariance for a consumer method, where it would be OK to accept a square as a rectangle.

EDIT: As this is getting upvoted and I said it slightly wrong: "where it would be OK to accept a rectangle where a square (or it's supertype, the rectangle) is acceptable." Goes ways to prove that strong typing is the better solution, though, I guess. :-)

Re: Inheritance Often Doesn't Make Sense

#53
post #35

Earlier quoted context omitted.

> and you can't just will it to have a different width and height and expect it to obey you through some force of nature I think you have let functional programming and immutable data-structures bias your world view. The real world is mutable and the wonder of the digital mutable world is that it is pretty much just will-alone that can set attributes as you describe. It is immutability that is a trendy but artificial…

No. Mutation essentially breaks subtyping. The infamous ArrayStoreException in Java is the story of how references (which should be invariant) are treated as covariant and therefore causes runtime exceptions.

Which is only because Java doesn't understand contravariance. In Scala, that problem no longer exists.

Re: Inheritance Often Doesn't Make Sense

#54
post #51

> Inheritance was never a problem: trying to use the same tree for three different concepts was the problem. I wonder why the author describes Polymorphism[1], but doesn't mention the term in any way. At the same time he uses the word 'abstract', but in a different way than any OO/Smalltalk programmer would do. While he mentions the 'Smalltalk blue book', I somehow do not trust his expertise in the field. [1]: https:…

> At the same time he uses the word 'abstract', but in a different way than any OO/Smalltalk programmer would do.

I doubt most people who define themselves as OO programmers, eg. working in C#/Java/C++/Python/etc... consider themselves on the Smalltalk side (eg message-passing) of the OO debate. 95% of actual programming experience in object-oriented languages is in languages of SIMULA descent.

Re: Inheritance Often Doesn't Make Sense

#55
post #33
post #12

Earlier quoted context omitted.

A common example that is used is when you have a Rectangle.setSize(int width, int height) method. When you make Square a subclass of Rectange, the Liskov substitution principle is violated. I think this (well-known) example is what the author of the article wants to refer to. This method is of course of type void. The problem is that by calling setSize, there is an implicit return type: the type of the object. This i…

> A common example that is used is when you have a Rectangle.setSize(int width, int height) method. Isn't the fact that we defined a method that changes both sizes at the same time the problem in this case? I mean, if we had two independent methods like `setBase(int)` and `setHeight(int)` this wouldn't be a problem at all (with the assumption, of course, that you accept the fact that the base changes when you change…

The same issue exists for Java Lists. Java lets you pass in subtypes of the type you specified in the List ie it treats them covariantly. This would be correct if the List was immutable but, as you can write to the List, you can blow the type relationship at runtime. In theory a List which you can both read and write should be invariant in the type parameter but this is useless in practice. So Java gives you a useful construct that isn’t completely type sound.

Re: Inheritance Often Doesn't Make Sense

#56
post #15

> A common counterexample to OO inheritance is the relationship between a square and a rectangle. Geometrically, a square is a specialisation of a rectangle: every square is a rectangle, not every rectangle is a square. Alternatively, you can argue that a rectangle is just a square with the additional freedom to vary the width and height independently. So in C++ syntax you would have: class Square { protected: size_t…

> There in fact is one correct answer but people often get it wrong. You need some serious appreciation of OO to know which.

Please enlighten me which one is correct, and I’ll happily argue that the other is in fact correct!

(I think you’re going to say Square extending Rectangle is correct lest Rectangle break Square’s invariants, but I’m not certain. What language you’re operating in may influence the matter; for there can be very important differences in how different languages handle variance which can invert the answer. I’m a little rusty on this, though, because I haven’t had to actually worry about it for a few years since I last wrote Rust code where the variance of a type with respect to a generic parameter actually mattered, and for work I’m mostly writing JavaScript where it’s all fuzzy enough that you pretty much get to decide what is right and what is wrong!)

Re: Inheritance Often Doesn't Make Sense

#57
post #15

> A common counterexample to OO inheritance is the relationship between a square and a rectangle. Geometrically, a square is a specialisation of a rectangle: every square is a rectangle, not every rectangle is a square. Alternatively, you can argue that a rectangle is just a square with the additional freedom to vary the width and height independently. So in C++ syntax you would have: class Square { protected: size_t…

Great post. I would go as far as claiming "don't really mess with inheritance until you have not internalized the principles of co- and contravariance". Might be a bit extreme, but I see no way how you can safely navigate OOP without (see Java's famous problems with getting this wrong on container types...).

Re: Inheritance Often Doesn't Make Sense

#58
post #25
post #22

Earlier quoted context omitted.

This is interesting. I find that inheritance as a code reuse tool pushes developers in the direction of taking whatever class they have and tuck some new properties to a subclass. On the other hand, if you realise that a square is a rectangle with the same width and height, why should you bother at all with creating a new class? A new constructor (or an helper function) should be all you need

> On the other hand, if you realise that a square is a rectangle with the same width and height, why should you bother at all with creating a new class? A new constructor (or an helper function) should be all you need What if you have a function that only knows how to operate on squares? Forcing it to accept Rectangles will be unnatural. Of course we can use just a single type Rectangle with two constructors, but inh…

> What if you have a function that only knows how to operate on squares? Forcing it to accept Rectangles will be unnatural

then you write another function that does what you want with rectangles. I think that most of the problems with inheritance stem from unnatural obsession for code reuse such as this.

Re: Inheritance Often Doesn't Make Sense

#59
post #35

Earlier quoted context omitted.

> and you can't just will it to have a different width and height and expect it to obey you through some force of nature I think you have let functional programming and immutable data-structures bias your world view. The real world is mutable and the wonder of the digital mutable world is that it is pretty much just will-alone that can set attributes as you describe. It is immutability that is a trendy but artificial…

No. Mutation essentially breaks subtyping. The infamous ArrayStoreException in Java is the story of how references (which should be invariant) are treated as covariant and therefore causes runtime exceptions.

If your array type (the only reified generic type in Java) is going to be parameterized by a single type, then you're right that it needs to be invariant.

One alternative would be to keep track of two types for an array: the most general type that can be stored into it and the most general type that might come back out of it. Any type cast that makes the types storable more strict or the type expected coming out less strict should be allowed. If you throw in a bottom type in your type system, then you get immutable arrays for free.

Of course, such a type system would probably cause a revolt among the majority of Java programmers for being too complex.

Post reply on HN