6
votes

I'm in the process of reading Programming in Scala, 2nd Edition (fantastic book, much better than the scala's website for explaining things in a non-rockety-sciencey manner) and I noticed this...oddity when going over Immutable and Mutable Sets.

It declares the following as an immutable set

var jetSet=Set("Boeing", "Airbus")
jetSet+="Lear"
println(jetSet.contains("Cessna"))

And then states that only Mutable sets define the += method. Ok that makes perfect sense. The problem is that this code works. And the type of set created when tested in the REPL is in fact the immutable set, but it has the += method defined on it and it functions perfectly fine. Behold

scala> var a = Set("Adam", "Bill")
a: scala.collection.immutable.Set[String] = Set(Adam, Bill)

scala> a += "Colleen"

scala> println(a)
Set(Adam, Bill, Colleen)

scala> a.getClass
res8: Class[_ <: scala.collection.immutable.Set[String]] = class scala.collection.immutable.Set$Set3

But if I declare the Set to be a val, the Immutable Set created does not have the += method defined

scala> val b = Set("Adam", "Bill")
b: scala.collection.immutable.Set[String] = Set(Adam, Bill)

scala> b += "Colleen"
<console>:9: error: value += is not a member of scala.collection.immutable.Set[String]
          b += "Colleen"

What is going on here? They both are stated to be an immutable Set but the one declared a var has access to the += method and can use it.

Also when I kept calling the getClass method on the var Immutable Set I noticed something strange....

scala> a.getClass
res10: Class[_ <: scala.collection.immutable.Set[String]] = class scala.collection.immutable.Set$Set3

scala> a += "One"

scala> a.getClass
res12: Class[_ <: scala.collection.immutable.Set[String]] = class scala.collection.immutable.Set$Set4

scala> a += "Two"

scala> a.getClass
res14: Class[_ <: scala.collection.immutable.Set[String]] = class scala.collection.immutable.HashSet$HashTrieSet

scala> a += "Tree"

scala> a.getClass
res16: Class[_ <: scala.collection.immutable.Set[String]] = class scala.collection.immutable.HashSet$HashTrieSet

scala> a
res17: scala.collection.immutable.Set[String] = Set(One, Tree, Bill, Adam, Two, Colleen)

My guess is that thanks to some hidden syntactic sugar, Scala recognizes that it's a Var and allows you to replace it with a newly constructed set anyway.

1
The Set is immutable, but a is mutable, adding an element to the set creates a new set and the result is assigned to a, if a is a val, it is immutable and you can't reassign to it.Ende Neu

1 Answers

15
votes

What's mutable is not the Set, is the reference to it.

a += "Colleen"

returns a new immutable set, assigned to the mutable variable a

Scala performs a syntactic transformation, turning the expression into

a = a + "Colleen"

in case += is not defined (as in the case of an immutable Set)

Clearly += doesn't make sense when a is a val, since you cannot reassign it, hence is forbidden.

Here's an extract taken from Programming in Scala (Section 17.3)

To make it easier to switch from immutable to mutable collections, and vice versa, Scala provides some syntactic sugar. Even though immutable sets and maps do not support a true += method, Scala gives a useful alternate interpretation to +=. Whenever you write a += b, and a does not support a method named +=, Scala will try interpreting it as a = a + b

If you keep reading in that section, you'll find a more thorough explanation, with examples.