3
votes

In the book "Programming in Scala", Third Edition, I saw an example that I could not understand. To understand it, I typed it into the scala interpreter:

scala> import scala.collection.mutable
import scala.collection.mutable

scala> val movieSet = mutable.Set("Hitch", "Poltergeist")
movieSet: scala.collection.mutable.Set[String] = Set(Poltergeist, Hitch)

scala> movieSet
res3: scala.collection.mutable.Set[String] = Set(Poltergeist, Hitch)  <<< res3

scala> movieSet += "Shrek"
res4: movieSet.type = Set(Poltergeist, Shrek, Hitch)                  <<< res4

My understanding was when doing += on a mutable.Set, the Set should mutate in-place (i.e. the variable to which it is assigned should not change), but the reference changed from res3 to res4. Also, I understood "val movieSet" to create a value that cannot be changed. Shouldn't this cause "val movieSet" to remain with the res3 reference, and not change to res4?

2

2 Answers

0
votes

The res3 and res4 are just additional references to the same object, generated by the scala shell whenever you type an expression with a non-unit return type but don't assign to a variable. When you do +=, the method mutates the set, and returns a reference to the same object again, so that's what res3 and res4 end up containing.

You can check that two values are actually the same exact object (not two equal objects) using the eq method. I.e., res3 eq movieSet and res4 eq movieSet should both be true.

val restricts the variable to always refer to the same object, but it does not restrict that object from mutating in whatever way.

0
votes

mutable.Set#+= is has signature

trait Set[A] {
  def +=(elem: A): this.type
}

That is, it adds an element, and then returns itself, presumably so you can chain calls like set += "a" += "b". The fact that the REPL generates a new val for the return is purely because it doesn't know any better. If you had typed in res3 after that, you would have seen the mutation.