| > I feel like the real Liskov Subsitution Principle should be "if you have a subtype P of type S, redesign your code so that there are no subtypes". Inheritance (a construct to allow you to create class hierarchies) is not the same thing as subtyping. For instance, an integer is a subtype of a real number (assuming both, in some hypothetical language, are distinct primitive types). In fact, LSP will work perfectly fine even in languages that don't support inheritance. LSP decries that that the behaviour of a particular operation matches the expressed intent of said operation. Going back to our integer being a subtype of real number example, the "<" operator will work perfectly fine whether any of the operands are either an integer or a real number. So, I definitely agree with the sentiment "class hierarchies are useless", but I can't say the same about LSP. In fact, if someone is passing me an object, I expect it to behave the way it does, as is decried by LSP. Also, you may argue that I am being pedantic, however I fear some people may take what you are implying a bit literally (e.g. never subtype anything), so I thought I'd clarify. |
If we're talking about theoretical "real numbers", then in constructive mathematics I believe "<" isn't decidable on reals. Whether that matters for the point you're trying to make, I have no idea.
If we're talking about floats, we have a concrete example of weird behavior of "<" leading to programming error: in Haskell, you can make a Set of any type that supports "<" (by virtue of being an instance of Ord - things that can be ordered). However, following ieee754, NaN is not equal to NaN. Which means that you can wind up accumulating arbitrarily many copies of NaN in your Set. (You don't usually want to be comparing floats for equality anyway, but it's still not good behavior.)