Scala: Understanding mutable variables with immutable collections

This is an excerpt from the Scala Cookbook (partially re-worded for the internet). This is Recipe 10.6, “Understanding Mutable Variables with Immutable Collections.”

Problem

You may have seen that mixing a mutable variable (var) with an immutable collection causes surprising behavior. For instance, when you create an immutable Vector as a var, it appears you can somehow add new elements to it:

scala> var sisters = Vector("Melinda")
sisters: collection.immutable.Vector[String] = Vector(Melinda)

scala> sisters = sisters :+ "Melissa"
sisters: collection.immutable.Vector[String] = Vector(Melinda, Melissa)

scala> sisters = sisters :+ "Marisa"
sisters: collection.immutable.Vector[String] = Vector(Melinda, Melissa, Marisa)

scala> sisters.foreach(println)
Melinda
Melissa
Marisa

How can this be?

Solution

Though it looks like you’re mutating an immutable collection, what’s really happening is that the sisters variable points to a new collection each time you use the :+ method.

The sisters variable is mutable — like a non-final field in Java — so it’s actually being reassigned to a new collection during each step. The end result is similar to these lines of code:

var sisters = Vector("Melinda")
sisters = Vector("Melinda", "Melissa")
sisters = Vector("Melinda", "Melissa", "Marisa")

In the second and third lines of code, the sisters reference has been changed to point to a new collection.

You can demonstrate that the Vector itself is immutable. Attempting to mutate one of its elements — which doesn’t involve reassigning the variable — results in an error:

scala> sisters(0) = "Molly"
<console>:12: error: value update is not a member of
scala.collection.immutable.Vector[String]
              sisters(0) = "Molly"
              ^

Summary

When you first start working with Scala, the behavior of a mutable variable with an immutable collection can be surprising. To be clear about variables and values:

  • A mutable variable (var) can be reassigned to point at new data.
  • An immutable variable (val) is like a final variable in Java; it can never be reassigned.

To be clear about collections:

  • The elements in a mutable collection (like ArrayBuffer) can be changed.
  • The elements in most immutable collections (like Vector and List) cannot be changed.

The Array does not obey the second statement about collections, so I used the word “most” in that sentence rather than “all.” Because the Scala Array is backed by a Java array, the elements in an Array can be changed after the Array is first created. You can’t make the Array longer or shorter, but you can re-assign an element, like this: elements(0) = "foo"

See Also

  • Recipe 20.2, “Prefer Immutable Objects”, discusses the use of mutable variables with immutable collections, and its opposite, using immutable variables with mutable collections as a “best practice.”