contravariant argument types: wanna?
Steven Schveighoffer
schveiguy at yahoo.com
Tue Sep 22 18:37:00 PDT 2009
On Tue, 22 Sep 2009 21:25:44 -0400, Jeremie Pelletier <jeremiep at gmail.com>
wrote:
> Steven Schveighoffer wrote:
>> On Tue, 22 Sep 2009 20:49:59 -0400, Jeremie Pelletier
>> <jeremiep at gmail.com> 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");
>>> }
>>> }
>> I don't know if this is possible:
>> A a = new C;
>> a.fun(new A); // oops, you just passed an A into a function which
>> requires a C!
>> Are you suggesting that the compiler insert dynamic cast checks
>> everywhere? Cause that seems like a lot of overhead...
>> -Steve
>
> Not everywhere, only where it detects covariant/contravariant overrides
> or implementations. In these cases you would already use explicit
> dynamic casts so the compiler generated code would just lower the
> required boilerplate.
I don't think it's worth the trouble. Dynamic casts are not as cheap as
implicit casts. Contravariance on parameters can be statically proven by
the compiler. I agree Andrei's example isn't that compelling (to be fair,
he did ask if anyone had a good example, indicating his wasn't), but there
are other examples that are more compelling (see the bug report I
referenced in a separate sub-thread).
For instance, if you only ever use class C, and never instantiate an A or
B instance, you still pay the dynamic cast penalty every time you call
fun! It doesn't sound to me like a good design.
I suppose you probably have run into this before, perhaps a real example
would be more convincing.
-Steve
More information about the Digitalmars-d
mailing list