contravariant argument types: wanna?

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Tue Sep 22 19:58:24 PDT 2009


Steven Schveighoffer wrote:
> On Tue, 22 Sep 2009 20:07:22 -0400, Andrei Alexandrescu 
> <SeeWebsiteForEmail at erdani.org> 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?
> 
> http://d.puremagic.com/issues/show_bug.cgi?id=3075
> 
> I thought Walter didn't want contravariance, maybe my clue was Walter 
> saying: "[Contravariance is] an attractive idea, but it's been 
> considered and rejected a couple of
> times now."
> 
> But he may just have been talking about only doing contravariance on 
> delegates, maybe he's all for contravariance in the general case, but I 
> didn't think so.
> 
> I think the bug above is the killer example, implicit casting of 
> delegates would be *awesome*.

Good point. Then I guess contravariance for overriding might be good for 
completeness' sake.

> BTW, I don't see a huge benefit from your example.  If B inherits from 
> A, then B knows about all the types A knows about (imagining an example 
> where the parameters were some other class hierarchy, like C and D), so 
> does it make a lot of sense to limit the arguments to B.fun to a base 
> class of something B must already know about?  I mean, it's not like B 
> doesn't know about the derived type, how hard would it be to just use 
> the derived type?  Maybe I'm missing something...

I don't think you're missing anything, or that we're missing the same 
thing. Contravariance is more of a philosophy thing and "the right way 
to go" in a type system with subtyping. Covariance and contravariance 
are an expression of the general principle "it's ok for an 
implementation to ask for less and offer more".

Practically, there may be cases in which the derived class wants to make 
clear that it only needs a more general parameter type. Because of that, 
you'd be able to issue calls that you otherwise can't. Consider:

class A { void fun(Y); }
static if (contravariance)
     class B : A { override void fun(X); }
else
     class B : A { override void fun(Y); }
class X { }
class Y : X { }
class Z : X { }

If what you have is a B and a Z, there is absolutely no way you could 
make the call B.fun(Z) without contravariance. Z is unrelated to Y and 
therefore casting it to a Y would throw.

Now the only issue is giving good names for A, B, X, Y, and Z :o).


Andrei



More information about the Digitalmars-d mailing list