contravariant argument types: wanna?

Steven Schveighoffer schveiguy at yahoo.com
Thu Sep 24 04:08:07 PDT 2009


On Wed, 23 Sep 2009 11:13:26 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail at erdani.org> 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



More information about the Digitalmars-d mailing list