On Wed, 23 Sep 2009 11:13:26 -0400, Andrei Alexandrescu
<[email protected]> wrote:
Jeremie Pelletier wrote:
Yigal Chripun wrote:
On 23/09/2009 03:07, Andrei Alexandrescu wrote:
Hello,
Today, overriding functions have covariant return types:
class A {
A clone();
}
class B : A {
B clone(); // fine, overrides A.clone
}
That is entirely principled and cool. Now the entire story is that
overriding function may have not only covariant return types, but also
contravariant argument types:
class A {
A fun(B);
}
class B : A {
B fun(A); // fine (in theory), overrides A.fun
}
Today D does not support contravariant arguments, but Walter told me
once he'd be quite willing to implement them. It is definitely the
right
thing to do, but Walter would want to see a compelling example before
getting to work.
Is there interest in contravariant argument types? If so, do you know
of
a killer example?
Thanks,
Andrei
consider:
class Car { ... }
class Truck : Car { ... }
class Driver {
void drive (Car c);
}
class truckDriver : Driver {
void drive(Truck t); // NOT contra-variant !!
}
does the above design will be affected by your suggestion?
You just described covariant arguments, which is a feature i'd also
like to see. It's different from contravariant arguments, implementing
one does not give the other unfortunately.
Well there's a good reason for it: contravariant arguments are sound,
covariant arguments aren't. My belief is that a design that would need a
lot of argument covariance ought to be analyzed.
I thought more about the car/truck example and I think it can be worked
out nicely. The problem right now is that Truck inherits Car. But a
truck is not substitutable for a car in all instances, because for
example a driver able to drive a car cannot necessarily drive a truck.
Here's a design that fixes that:
class AutoVehicle { ... }
class Car : AutoVehicle { ... }
class Truck : AutoVehicle { ... }
class Driver {
// A driver is licensed to drive a car
void drive(Car c);
}
class TruckDriver : Driver {
// A truck driver is licensed to drive a car...
override void drive(Car c);
// ... and a truck
void drive(Truck c);
// No contravariance needed yet
}
class JamesBond : Driver {
// James Bond can drive any auto vehicle
// Contravariance needed here
override void drive(AutoVehicle c) { ... }
}
Now if what you have is a JamesBond and a Truck, you need contravariance
to have him drive it. (A HotGirl may or may not be present in the scene.)
Your example just triggered a possible problem with contravariance.
Consider this class:
class Bad : TruckDriver {
override void drive(AutoVehicle c) { ...}
}
What does this override, drive(Truck) or drive(Car), or both? What if you
didn't want to override both? My instinct is that this should be an
error, to keep things simple. But it might be very annoying for some
designs...
-Steve