Hi,
Going through Scala tutorial, I got stuck at the Variance topic.
While I was trying to improve the content, I found myself might not have been able to grasp the difference between thee two. So I was adviced by SethTisue to post my questions/understanding here before proceeding with pull request.
So here is my understanding:
As far as the Covariance & Contravariance go, I’ve always looked at them as being analogous to UpperBound & LowerBound, respectively.
So, I understand that:
- Covariance : allows assigning an instance to a variable whose type is one of the instance’s generic type; i.e. supertype .
- Contravariance : allows assigning an instance to a variable whose type is one of the instance’s derived type; i.e. subtype .
The same rule applies when passing to a method parameter.
For Covariance
List[+A]
was used as an example. Replacing the type with a concrete example:
List[Animal] // So here A = Animal
The Covariant of A
must be a subtype of A
say B
. A
is an UpperBound.
So I understand that the following must hold true:
List[Animal] :> List[Cat] \\ where A = Animal & B = Cat
List[Animal] :> List[Dog] \\ where A = Animal & B = Dog
So, this brings me to the conclusion that A
is a supertype of B
, or in other words B
is a subtype of A
. Therefore,
val animals:List[Animal] = List[Cat]() \\ holds true!
From the tutorial, I get stuck when I read the description:
“making A
covariant implies that for two types A
and B
where A
is a subtype of B
…” <=
If A
is a covariance, why would it be assumed as a subtype of B
? Shouldn’t it be the way around?
And if the wording of “subtype” is more natural, Would saying B
is a subtype of A
make sense?
For Contravariance
I’m not sure if it’s the verbiage that’s confusing me, but looking at it for a while I see why you insisted my correction was wrong.
I wonder if having a verbiage like the following would make it less confusing:
Let’s take List[-A]
as an example. Replacing the type with a concrete example:
List[Cat] // So here A = Cat
The Contravariant of A
must be a supertype of A
say B
. A
is a LowerBound.
So I understand that the following must hold true:
List[Cat] :< List[Animal] \\ where A = Cat & B = Animal
List[Dog] :< List[Animal] \\ where A = Dog & B = Animal
So, this brings me to the conclusion that A
is a subtype of B
, or in other words, B
is a supertype of A
. Therefore (via printer example),
val catPrinter: Printer[Cat] = new Printer[Animal]{...} \\ holds true!
Could anyone advice on whether I’m on the right path in understanding the differences?
Thanks
Husain