3

I was writing some code today when I noticed this quirky behavior. It appears that immutable maps nested within mutable maps allow for the (usually mutable) += operator.

scala> val myMutableMap = mutable.Map[String, scala.collection.immutable.Map[String, String]]()
myMutableMap: scala.collection.mutable.Map[String,scala.collection.immutable.Map[String,String]] = Map()

scala> myMutableMap += "outerkey" -> scala.collection.immutable.Map("k1"-> "v1")
res25: myMutableMap.type = Map(outerkey -> Map(k1 -> v1))

scala> myMutableMap("outerkey") += "k2"->"v2"

scala> myMutableMap
res27: scala.collection.mutable.Map[String,scala.collection.immutable.Map[String,String]] = Map(outerkey -> Map(k1 -> v1, k2 -> v2))

scala> val huhwhat = myMutableMap("outerkey")
huhwhat: scala.collection.immutable.Map[String,String] = Map(k1 -> v1, k2 -> v2)

scala> huhwhat += "k3"->"k4"
<console>:21: error: value += is not a member of scala.collection.immutable.Map[String,String]
              huhwhat += "k3"->"k4"

I looked into the Map.scala source, but didn't see any obvious answers to where the += operator could be inherited from.

This is on Scala 2.11.5. Does anyone know what's going on?

pavelmk
  • 53
  • 4

1 Answers1

3
myMutableMap("outerkey") += "k2"->"v2"

is being interpreted by the compiler as

myMutableMap("outerkey") = myMutableMap("outerkey") + ("k2"->"v2")

i.e., create a new map from the immutable myMutableMap("outerkey") with the added key ("k2"->"v2"), and update myMutableMap with the new pair ("outerkey" -> <the new map>).

This is implemented by mutable.MapLike's update method, which provides the foo(x) = y behaviour. See this blog post for more info (and there's also an SO answer listing a bunch of other magic functions in Scala).

Community
  • 1
  • 1
nornagon
  • 15,393
  • 18
  • 71
  • 85
  • 1
    To be more precise: if Scala doesn't find a suitable `+=` (or `-=` …) method, it will retry again with `foo = foo + bar` and use that, if it type-checks. `foo(bar) = baz`, then, in turn, is interpreted as `foo.update(bar, baz)`, it's basically the "setter" equivalent of `apply`. – Jörg W Mittag Nov 04 '15 at 23:31