I have read this and this answers before posting this question, but I am still a bit unclear in my understanding of this topic as explained below:
I understand what covariant and contravariant independently mean.
If I have classes as below:
class Car {}
class SportsCar extends Car {}
class Ferrari extends SportsCar {}
And:
object covar extends App {
// Test 1: Works as expected
def test1( arg: SportsCar => SportsCar ) = {
new SportsCar
}
def foo1(arg: Car): Ferrari = { new Ferrari }
def foo2(arg: SportsCar): Car = { new Ferrari }
def foo3(arg: Ferrari): Ferrari = { new Ferrari }
test1(foo1) // compiles
test1(foo2) // Fails due to wrong return type - violates return type is covariant
test1(foo3) // Fails due to wrong parameter type - violates param type is contravariant
// Test 2: Confused - why can I call test2 succesfully with ferrari
// as the parameter, and unsuccesfully with a car as the parameter?
def test2(arg: SportsCar): SportsCar = {
new Ferrari
}
val car = new Car()
val sportsCar = new SportsCar()
val ferrari = new Ferrari()
val f1 = test2(ferrari) // compiles - why?
val f2 = test2(car) // fails - why?
}
As mentioned above, in the Test 2, why can I call test2 succesfully with ferrari as the parameter, and unsuccesfully with a car as the parameter?
Does the statement Functions are contravariant in their argument types and co-variant in their return types only apply to functions which are passed as arguments? I guess I am not making appropriate distinction between the statement and the 2 tests that I wrote.
That's because you're mixing subtyping with co/contravariance.
In your first example you expect a Function1[-T, +R]
. In that case, the rules of co/contravariance apply to the function type. In your second example, you follow simple subtyping rules.
In test1
, where you pass in foo1
which is Car => Ferrari
everything works because foo1
expects a Car
, but gets a SportsCar
, which is a Car
. Any method that requires a Car
can deal with a subtype because of the subtyping nature. But these rules don't work when we talk about subtyping alone.
If we expand test1
with foo1
with the actual types, perhaps it'll be clearer:
Car
SportsCar
SportsCar
Ferrari
Everything lines up well.
In test2
the rules change. If I expect a SportsCar
you pass in any car, then I can no longer depend on the input being a sports car and everything breaks, but if you pass in a ferrari which is actually a sports car, everything is fine.
Again, let's line up the types:
SportsCar
Ferrari
(subtype)SportsCar
Car
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With