Why I can't assign to the mutable attribute?
class X[ T](t: T) {
var _t: T = t // Error. Ok if this is val.
}
The error is:
covariant type T occurs in a contravariant position in type T of value _t_=
var _t: T = t
I try to think about this.
- Suppose
B <: A
, which meansclass B
is a subclass ofclass A
. - Then,
X[B] <: X[A]
. - I can assign an
object xb
ofclass X[B]
to aninstance xa
ofclass X[A]
. - Then we assign
xb._t (type B)
tovar _t: A
inxa
. - This seems to be fine.
What is wrong here?
CodePudding user response:
Your assumption is correct. Not only you can assign something of type A
, but you could also assign something of type A
which would not be of type B
. So you would end up violating the type soundness at runtime.
Here's a more easy-to-follow example. Suppose you have the nonvariant Cell
type:
class Cell[T](elem: T) {
var curr: T = elem
}
Now assume for a moment that this type was declared covariant instead, as in Cell[ T]
- and that this code would pass the Scala compiler type check (it doesn't). Then you could write the following problematic code:
val c1 = new Cell[String]("abc")
val c2: Cell[Any] = c1
c2.curr = 1
val s: String = c1.curr // violates the type soundness at runtime
If Cell
would be covariant, taken individually, each line of code would compile just fine. But taken together as a whole, these lines end up assigning an Int
to a String
. This would clearly throw a ClassCastException
at runtime.
So even though the faulty line is the last one, actually it is the second line the one to blame, because the other ones are just too simple and too fundamental. In natural language, this would translate as follows:
A
Cell
ofString
is not also aCell
ofAny
because there are things you can do with aCell
ofAny
that you cannot do with aCell
ofString
. For example, you cannot assign anInt
to thecurr
field of aCell
ofString
.
This is why, if you would make Cell
covariant, your code would fail to with the same error:
error: covariant type T occurs in contravariant position in type T of value curr_=
var curr: T = elem