3 ms·
"what is unsound is supporting mutations that alter identity" EDIT: included code for clarity I can make essentially the same example with concrete entities t
by jeffdavis 13y ago
"what is unsound is supporting mutations that alter identity"
EDIT: included code for clarity
I can make essentially the same example with concrete entities that exist in time and space, as well:
void f1() {
GasolineVehicle vehicle(myEngine);
f2(vehicle);
cout << vehicle.getMPG() << "\n";
}
void f2(Vehicle &v) {
if (...)
v.setPropulsionDevice(myMotor);
}
After replacing the engine with a motor, MPG no longer makes sense.
At the time you're writing the Vehicle class, it seems perfectly reasonable to define a setPropulsionDevice() method, and there's no hint that calling it would change the vehicle's identity. Only when you define the subclass does it turn into an identity problem, but then it's too late.
Also, if you think the Circle/Ellipse example is not compelling enough, how would you structure those two classes? Would one inherit from the other, and if so, in which direction?
- zvrba 13y ago> At the time you're writing the Vehicle class, it seems perfectly reasonable to define a setPropulsionDevice() method Are you claiming that it seems reasonable to be able to replace a car engine with a jet engine without changing anything else? (The car's hull, construction, etc., and after all these modifications it's not the "same" car anymore.)
- jeffdavis 13y agoNo. Any example I come up with is going to sound contrived, because it is. Real examples get messy, and just add to the confusion. But real issues show up due to fundamental problems with "is-a": Even if an X value "is-a" Y value, an X variable is not a Y variable (because it can't hold all objects of type Y). So passing a mutable object reference to a function is fundamentally different than passing an immutable object -- but inheritance hierarchies treat them the same. You can get around this problem by not using inheritance hierarchies that work that way, or by always using immutable objects (value semantics). But I pretty much consider the Java/C++-style inheritance to be a mess.
- dragonwriter 13y ago> But real issues show up due to fundamental problems with "is-a": Even if an X value "is-a" Y value, an X variable is not a Y variable (because it can't hold all objects of type Y). So passing a mutable object reference to a function is fundamentally different than passing an immutable object -- but inheritance hierarchies treat them the same. That's not a problem with "is-a", its a problem with unsound use of mutation in inheritance heirarchies. A mutating method can either: 1) Anticipate that it can fail, or at least the mutation component can (however that is signaled, whether by return value or exception), on some combinations of argument values and object state, in which case it works fine as a method anywhere in an inheritance hierarchy, or 2) Be guaranteed to succeed completely with any arguments of the appropriate types, in which case its fundamentally unsound anywhere except in a final class (since declaring a mutating method is essentially logically equivalent to declaring a method with a -- potentially additional -- return value whose type is simultaneously both the the type of the class it is declared in and the type of the class it is used in.
- mbrock 13y agoI think it's a fundamentally bad idea to think of class hierarchies as ontological schemata of real world things. It's like ancient philosophers trying to define the human being. Are we FeatherlessBipeds or RationalAnimals? Who really cares? The Circle/Ellipse quabble is academic because it never mentions the actual domain – for what purpose are we trying to model these entities in the first place? Even in pure geometry, there are many different ways to classify and treat these structures. If you have a generic shape representation, you could just have Circle and Ellipse as different builder functions. If you're mostly just concerned with drawing, then a simple "isCircular" method on the Ellipse object might be what you need. And so on...