On Tue, 22 Sep 2009 21:25:44 -0400, Jeremie Pelletier <[email protected]>
wrote:
Steven Schveighoffer wrote:
On Tue, 22 Sep 2009 20:49:59 -0400, Jeremie Pelletier
<[email protected]> 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