I am new in Scala world and now I am reading the book called "Scala in Action" (by Nilanjan Raychaudhuri), namely the part called "Mutable object need to be invariant" on page 97 and I don't understand the following part which is taken directly from the mentioned book.
Assume ListBuffer is covariant and the following code snippet works without any compilation problem:
scala> val mxs: ListBuffer[String] = ListBuffer("pants")
mxs: scala.collection.mutable.ListBuffer[String] =
ListBuffer(pants)
scala> val everything: ListBuffer[Any] = mxs
scala> everything += 1
res4: everything.type = ListBuffer(1, pants)
Can you spot the problem? Because everything is of the type Any, you can store an integer value into a collection of strings. This is a disaster waiting to happen. To avoid these kinds of problems, it’s always a good idea to make mutable objects invariant.
I would have the following questions..
1) What type of everything
is in reality? String
or Any
? The declaration is "val everything: ListBuffer[Any]
" and hence I would expect Any
and because everything should be type of Any
then I don't see any problems to have Integer
and String
in one ListBuffer[Any]
. How can I store integer value into collection of strings how they write??? Why disaster??? Why should I use List (which is immutable) instead of ListBuffer (which is mutable)? I see no difference. I found a lot of answers that mutably collections should have type invariant and that immutable collections should have covariant type but why?
2) What does the last part "res4: everything.type = ListBuffer(1, pants)
" mean? What does "everything.type" mean? I guess that everything
does not have any method/function or variable called type
.. Why is there no ListBuffer[Any] or ListBuffer[String]?
Thanks a lot,
Andrew
I agree with most of what @Andrey is saying I would just add that covariance and contravariance belong exclusively to immutable structures, the exercisce that the books proposes is just a example so people can understand but it is not possible to implement a mutable structure that is covariant, you won't be able to make it compile. As an exercise you could try to implement a
MutableList[+A]
, you'll find out that there is not way to do this without tricking the compiler puttingasInstanceOf
everywhere1 This doesn't look like a single question, so I have to subdivide it further:
everything
isListBuffer[_]
, with erased parameter type. Depending on the JVM, it holds either 32 or 64 bit references to some objects. The typesListBuffer[String]
andListBuffer[Any]
is what the compiler knows about it at compile time. If it "knows" two contradictory things, then it's obviously very bad."I don't see any problems to have Integer and String in one ListBuffer[Any]". There is no problem to have
Int
andString
inListBuffer[Any]
, becauseListBuffer
is invariant. However, in your hypothetical example,ListBuffer
is covariant, so you are storing anInt
in aListBuffer[String]
. If someone later gets anInt
from aListBuffer[String]
, and tries to interpret it asString
, then it's obviously very bad."How can I store integer value into collection of strings how they write?" Why would you want to do something that is obviously very bad, as explained above?
"Why disaster???" It wouldn't be a major disaster. Java has been living with covariant arrays forever. It's does not lead to cataclysms, it's just bad and annoying.
"Why should I use List (which is immutable) instead of ListBuffer (which is mutable)?" There is no absolute imperative that tells you to always use
List
and to never useListBuffer
. Use both when it is appropriate. In 99.999% of cases,List
is of course more appropriate, because you useList
s to represent data way more often than you design complicated algorithms that require local mutable state of aListBuffer
."I found a lot of answers that mutably collections should have type invariant and that immutable collections should have covariant type but why?". This is wrong, you are over-simplifying. For example, intensional immutable sets should be neither covariant, nor invariant, but contravariant. You should use covariance, contravariance, and invariance when it's appropriate. This little silly illustration has proven unreasonably effective for explaining the difference, maybe you too find it useful.
2 This is a singleton type, just like in the following example:
Here is a longer discussion about the motivation for this.