Here is a concrete example of what I'm asking about:
MutableList[ImmutableList[Object]]
Where ImmutableList
is covariant wrt its first parameter, but MutableList
is invariant wrt its first parameter. Object is our "root type" that all types are a subtype of.
Is the type MutableList[ImmutableList[String]]
a subtype of MutableList[ImmutableList[Object]]
(assuming String is a subtype of Object) or is the only subtype of MutableList[ImmutableList[Object]]
another MutableList[ImmutableList[Object]]
? Is this language specific?
Any insights or articles are very much appreciated.
1 Answer 1
TL;DR No, MutableList[ImmutableList[String]]
is not a subtype of MutableList[ImmutableList[Object]]
.
You can approach the problem intuitively by thinking about what's possible to do with the type. If MutableList
has a "push" operation, for instance:
- You can push an
ImmutableList[Object]
into aMutableList[ImmutableList[Object]]
. ImmutableList[Integer]
is a subtype ofImmutableList[Object]
.- Therefore, you can push an
ImmutableList[Integer]
into aMutableList[ImmutableList[Object]]
. - However, you cannot push an
ImmutableList[Integer]
into aMutableList[ImmutableList[String]]
; - therefore,
MutableList[ImmutableList[String]]
is not a subtype ofMutableList[ImmutableList[Object]]
.
This really is an explanation of why MutableList[T]
is invariant in T
: because it supports push
.
It's also possible to work out the answer using the definitions of covariance and invariance.
ImmutableList[T]
is covariant in T
. This means that if A <: B
, then ImmutableList[A] <: ImmutableList[B]
. (I'm using the symbol <:
for "is a subtype of")
- Take
A = String
andB = Object
.String <: Object
, soImmutableList[String] <: ImmutableList[Object]
.
MutableList[T]
is invariant in T
. This means that even if A <: B
, MutableList[A]
and MutableList[B]
do not participate in a subtype relationship; they are incompatible types.
- Take
A = ImmutableList[String]
andB = ImmutableList[Object]
. Even thoughImmutableList[String] <: ImmutableList[Object]
(as we showed above),MutableList[ImmutableList[String]]
andMutableList[ImmutableList[Object]]
still do not participate in a subtype relationship.
Covariance and contravariance are two ways the subtype relationship can "leak out" of a type constructor. Invariance is what happens when the subtype relationship doesn't leak at all, so you can't cascade invariance with anything else and get anything other than invariance.
The meaning of different kinds of variance is not language specific, but there are some languages with mutable containers that are not considered invariant. Java is one example, and you can abuse this loophole in the type system to make programs that fail with runtime type errors despite compiling fine.
-
1The point is that you can add an
ImmutableList[Object]
to aMutableList[ImmutableList[Object]]
but not to aMutableList[ImmutableList[String]]
. So the later doesn't comply with the contract of the former.Florian F– Florian F2020年08月28日 09:03:02 +00:00Commented Aug 28, 2020 at 9:03 -
@FlorianF Thanks, I like your more intuitive explanation, so I overcomplicated it and added it to the answer :-)trent– trent2020年08月28日 14:19:05 +00:00Commented Aug 28, 2020 at 14:19
Explore related questions
See similar questions with these tags.