1

Consider the following:

scala> val myset = Set(1,2)
myset: scala.collection.immutable.Set[Int] = Set(1, 2)

scala> myset += 3
<console>:9: error: reassignment to val
              myset += 3
                    ^

scala> var myset = Set(1,2)
myset: scala.collection.immutable.Set[Int] = Set(1, 2)

scala> myset += 3

scala> myset
res47: scala.collection.immutable.Set[Int] = Set(1, 2, 3)

In one case I can add "alex" and in another I can't. I know the difference between val and var. However what confused me in both cases Scala tells me that the Set is immutable but it allows different behaviour.

I don't want to just as that's because in oncase myset is a val and in one it is a var. I want a deeper answer than that to explain why in both cases Scala says myset is an immutable set but yet treats both differently. Because it is counter intuitive.

For example, is there any difference using a mutuable set and declaring an immutable set as var? And why does scala let you bend the rules? Would it not be better if when it is said immutable it meant it?

Thanks.

dublintech
  • 16,815
  • 29
  • 84
  • 115

3 Answers3

3

First of all let's translate the += call

val myset = Set(1,2)
myset = myset + 3 //this is what the compiler does for myset += 3

This means that we're actually calling the + method on Set, whose scaladoc says

Creates a new set with an additional element, unless the element is already present.

What the code is trying to do is therefore to create a new Set with the added element and reassign it to the immutable reference myset.

Now if we change the reference to a mutable one (using var) then you can reassign it with the newly made and immutable Set(1,2,3)

The original Set(1, 2) is still immutable and no rule is broken. Let's explain this with a case

var myset = Set(1,2)
val holdIt = Some(myset)
myset += 3
println(myset) // will print Set(1, 2, 3)
println(holdIt)// will print Some(Set(1, 2))

As you can see, the original Set, which is captured by the Option, was never changed, we just reassigned it's variable reference to a newly created Set

pagoda_5b
  • 7,333
  • 1
  • 27
  • 40
2

When you use an immutable Set, a new Set will be created and reassigned to the var, which is not possible when using val. But a mutable Set can be mutated. While the following is not a perfectly save demonstration, it still shows that with an immutable Set a new object is created, and the mutable Set instance stays the same:

scala> var set1 = Set(1, 2)
set1: scala.collection.immutable.Set[Int] = Set(1, 2)

scala> System.identityHashCode(set1)
res0: Int = 2119337193

scala> set1 += 3

scala> System.identityHashCode(set1)
res2: Int = 1866831260

scala> var set2 = collection.mutable.Set(1, 2)
set2: scala.collection.mutable.Set[Int] = Set(2, 1)

scala> System.identityHashCode(set2)
res3: Int = 594308521

scala> set2 += 3
res4: scala.collection.mutable.Set[Int] = Set(2, 1, 3)

scala> System.identityHashCode(set2)
res5: Int = 594308521
Leo
  • 37,640
  • 8
  • 75
  • 100
2

Consider myset as the pointer and Set(1,2) as the object

In case of val myset pointer is assigned only once & cannot point to different object (i.e. pointer is immutable) where as in case of var myset can be assigned arbitrary times & can point to different objects (i.e. pointer is mutable)

In both cases object Set(1,2) is immutable.

When you do

scala> val myset = Set(1,2)
myset: scala.collection.immutable.Set[Int] = Set(1, 2)

scala> myset += 3
<console>:9: error: reassignment to val
              myset += 3
                    ^

myset += 3 actually gives you a new Set with 3 appended to it..

Since the myset pointer is immutable you cannot reassign to the new object returned by the + method.

where as in case of

scala> var myset = Set(1,2)
myset: scala.collection.immutable.Set[Int] = Set(1, 2)

scala> myset += 3

Since myset pointer is mutable you can reassign to the new object returned by the + method.

Shrey
  • 2,374
  • 3
  • 21
  • 24