Skip to content

Commit 5bfd57c

Browse files
authored
Merge pull request #2468 from KarahanS/patch-1
Subsection for Immutability and Variance
2 parents 6cfaf58 + c737b64 commit 5bfd57c

File tree

1 file changed

+24
-2
lines changed

1 file changed

+24
-2
lines changed

_tour/variances.md

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ abstract class Serializer[-A] {
169169
}
170170

171171
val animalSerializer: Serializer[Animal] = new Serializer[Animal] {
172-
def serialize(animal: Animal): String = s"""{ "name": "${animal.name}" }"""
172+
def serialize(animal: Animal): String = s"""{ "name": "${animal.name}" }"""
173173
}
174174
val catSerializer: Serializer[Cat] = animalSerializer
175175
catSerializer.serialize(Cat("Felix"))
@@ -191,7 +191,29 @@ catSerializer.serialize(Cat("Felix"))
191191

192192
We say that `Serializer` is *contravariant* in `A`, and this is indicated by the `-` before the `A`. A more general serializer is a subtype of a more specific serializer.
193193

194-
More formally, that gives us the reverse relationship: given some `class Contra[-T]`, then if `A` is a subtype of `B`, `Contra[B]` is a subtype of `Contra[A]`.
194+
More formally, that gives us the reverse relationship: given some `class Contra[-T]`, then if `A` is a subtype of `B`, `Contra[B]` is a subtype of `Contra[A]`.
195+
196+
### Immutability and Variance
197+
Immutability constitutes an important part of the design decision behind using variance. For example, Scala's collections systematically distinguish between [mutable and immutable collections](https://docs.scala-lang.org/overviews/collections-2.13/overview.html). The main issue is that a covariant mutable collection can break type safety. This is why `List` is a covariant collection, while `scala.collection.mutable.ListBuffer` is an invariant collection. `List` is a collection in package `scala.collection.immutable`, therefore it is guaranteed to be immutable for everyone. Whereas, `ListBuffer` is mutable, that is, you can change, add, or remove elements of a `ListBuffer`.
198+
199+
To illustrate the problem of covariance and mutability, suppose that `ListBuffer` was covariant, then the following problematic example would compile (in reality it fails to compile):
200+
201+
{% tabs immutability_and_variance_2 %}
202+
{% tab 'Scala 2 and 3' %}
203+
```scala mdoc:fail
204+
import scala.collection.mutable.ListBuffer
205+
206+
val bufInt: ListBuffer[Int] = ListBuffer[Int](1,2,3)
207+
val bufAny: ListBuffer[Any] = bufInt
208+
bufAny(0) = "Hello"
209+
val firstElem: Int = bufInt(0)
210+
```
211+
{% endtab %}
212+
{% endtabs %}
213+
214+
If the above code was possible then evaluating `firstElem` would fail with `ClassCastException`, because `bufInt(0)` now contains a `String`, not an `Int`.
215+
216+
The invariance of `ListBuffer` means that `ListBuffer[Int]` is not a subtype of `ListBuffer[Any]`, despite the fact that `Int` is a subtype of `Any`, and so `bufInt` cannot be assigned as the value of `bufAny`.
195217

196218
### Comparison With Other Languages
197219

0 commit comments

Comments
 (0)