contravariant argument types: wanna?
Jeremie Pelletier
jeremiep at gmail.com
Tue Sep 22 18:21:10 PDT 2009
Jeremie Pelletier wrote:
> 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
>
> I can't think of an use for contravariant parameters, since a B is
> guaranteed to always be a A, I don't see the point of being able to
> declare fun(A).
>
> However, I would love to hear about covariant parameters, it would be
> most useful for interface implementations:
>
> interface A {
> A fun(A);
> }
> class B : A {
> B fun(B);
> }
> class C : A {
> C fun(C);
> }
>
> Currently you need some pretty boring boilerplate code, which isn't
> complicated but gets repetitive when you have hundreds of such cases:
>
> class B : A {
> B fun(A) {
> if(B b = cast(B)b) // do stuff
> else throw Error("Invalid object type");
> }
> }
>
> Jeremie
Also about contravariance, using your original A and B objects, what
would happen in the following case:
void main() {
A a = new B;
a.fun(new A); // Error! (in theory)
}
Should the compiler disallow the call or assume there can be a
contravariant overload? what happens if theres another subclass
requiring a B argument in the a.fun(new A); call.
With covariant arguments you just need to let the compiler generate the
boilerplate in the method's prolog and not have to worry about other
implementations, so long as they are also all covariant.
For contravariant arguments you'd need special prolog code in every
method in order to allow the above case. Unless you just disallow it and
enforce the contravariance only when calling directly into a B or its
subclasses.
Writing this last paragraph made me realize contravariance could be
useful to alter the required object in a subclass chain:
class A { A foo(B); } // can be called using B or C only
class B : A { override A foo(A); } // can be called using A, B, C or D
class C : B { override A foo(C); } // can be called using C only
class D : A { override A foo(D); } // can be called using D only
Here you would only need special prolog code for B, C, and D to perform
a dynamic cast if needed.
More information about the Digitalmars-d
mailing list