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.