Hacker News new | past | comments | ask | show | jobs | submit login

This just comes down to the difference between identity that changes with mutation, and identity which is constant with mutation.

What you say is true RE the OP, but the OP is not very good. Mutability is a red herring. Uncle Bob points this out in his response.

Assume immutability. Now the names 'square' and 'rectangle' actually make sense and the real issue becomes clear: the correct number and names of variables needed for representation. And the definition of the 'area' method.

Circle-ellipse is a better illustration because a circle has a 'radius' property that an ellipse doesn't have.




Ellipses have major and minor radii; in circles, both are the same length. Attributes in these kinds of objects are a bit of a red herring, though, as one set of attributes can be calculated from another, and vice versa. A circle can be described as a parametric function of angle, x = r * cos(t), y = r * sin(t); or the more familiar r^2 = x^2 + y^2. Which attributes you choose as the canonical, and which you choose to calculate, is just an implementation detail.

FWIW, I've felt for some time that the benefits of object orientation have been over-emphasized, though the general pattern of industry and language development has been moving in a better direction of late (say past 5 years).

I also have no well of respect for Uncle Bob - that's not to say he isn't a very intelligent, wise guy with lots of insightful things to say, it's just that I haven't read much of his stuff, and had never heard of him until a few months ago. But when I read him try to make an analogy between types in a program representing things in the world, and two lawyers advocating for a divorcing couple, and saying that since it's not the lawyers that are divorcing, you shouldn't model your types in an isomorphic correspondence to the things in the world you're modelling, well then, I think he's overreaching. If all your world is immutable rectangles, and you need to handle squares differently, and you're solidly orthodox in your subscription to OO, then I think modelling the relationship with inheritance is perfectly fine. In static languages, the fact that inheritance creates a subtyping relationship and introduces the possibility of polymorphism and dynamic dispatch is usually more interesting than the inherited scope of member lookup.


Ellipses have major and minor radii; in circles, both are the same length.

This is exactly the issue. According to specialization by constraint when an ellipse has equal radii then it is both an ellipse and a circle, therefore a circle isa ellipse, therefore a circle is a subtype of ellipse.

Hence a square isa rectangle. The specialization by constraint argument is that any type system that requires a rectangle to be a subtype of square or both to be subtypes of polygon is inherently broken.

LSP says that if a circle is a subtype of ellipse then it can be used in place of an ellipse. But it can't, so a circle can't be a subtype of ellipse. LSP and specialization by constraint are not compatible.


LSP says that if a circle is a subtype of ellipse then it can be used in place of an ellipse. But it can't

You haven't shown that - you've merely asserted it. If you live in a world of immutable ellipses, and you need different behaviour from circles and non-circle ellipses, you can make circles subtypes of ellipses without any problems, and still satisfy LSP - because we're not specializing by constraint. The most important point: circleness is attribute of an ellipse, and if the ellipse is immutable - its radii can't be changed - then that circleness can be contained in the type without problem.


You haven't shown that

I don't understand. An ellipse contains more information than a circle. You can't ask a circle for the length of its minor radius, so how can it substitute for an ellipse? I'm not sure we're speaking the same language.


I believe you're assuming more things about the programmatic interface of a circle type than is necessarily true. An ellipse with equal major and minor radii is a circle. That much is mathematical fact.

Equivalently, a circle is a special kind of ellipse. As an ellipse, you can certainly ask it for major and minor radii. But "more information" is an implementation detail. If Circle were a subclass of AbstractEllipse, which itself also had a concrete Ellipse subclass, Circle might implement both major and minor radii "properties" in terms of a single field. But again, this is just an implementation detail.

All this is only practically useful if there's a reason for considering circles as ellipses, or for specializing behaviour for circles. For example, the circumference calculation for circles is much simpler than for ellipses, so we might want to specialize it. Since our values are immutable, there's no problem in capturing this optimization in the type and overriding the appropriate method.

(I'm assuming immutable values throughout, to make it clear.)


You seem to be saying that circles are not subtypes of ellipses. That circleness is just a property of ellipses. If we accept that then the whole issue is moot, and substitutability is true by tautology.

A circle is a triple: (x,y,radius). An ellipse is a 5-tuple: (x,y,major,minor,angle).

Maybe we should be talking about angle instead? It can't be derived from a circle. What happens when you feed a circle to selectEllipsesByAngle()?


If isEllipseAtAngle(someAngle): boolean were a method, a Circle type could return true for every angle.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: