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.<hr>
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.<hr>
I would have the following questions..
1) What type of
everything is in reality?
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
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,
<strong>1</strong> This doesn't look like a single question, so I have to subdivide it further:<ol> <li>"In reality"
ListBuffer[_], with erased parameter type. Depending on the JVM, it holds either 32 or 64 bit references to some objects. The types
ListBuffer[Any]is what the compiler knows about it at compile time. If it "knows" two contradictory things, then it's obviously very bad.</li> <li>
"I don't see any problems to have Integer and String in
one ListBuffer[Any]". There is no problem to have
ListBuffer is invariant. However, in your hypothetical example,
ListBuffer is covariant, so you are storing an
Int in a
ListBuffer[String]. If someone later gets an
Int from a
ListBuffer[String], and tries to interpret it as
String, 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?</li> <li>
"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.</li> <li>
"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 use
ListBuffer. Use both when it is appropriate. In 99.999% of cases,
List is of course more appropriate, because you use
Lists to represent data way more often than you design complicated algorithms that require local mutable state of a
"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.</li> </ol>
<strong>2</strong> This is a singleton type, just like in the following example:
scala> val x = "hello" x: String = hello scala> val y: x.type = x y: x.type = hello
Here is a longer discussion about the motivation for this.
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 putting