Mahdi
Mahdi

Reputation: 817

Adding an item to scala.collection.mutable.HashMap

I tried to add an element to a Scala HashMap

val c2 = new collection.mutable.HashMap[String,Int]()
c2 += ("hh",1)

but the above gives me a compile error.

[error]  found   : String("hh")
[error]  required: (String, Int)
[error]   c2 += ("hh", 1)
[error]          ^
[error] /scalathing/Main.scala:5: type mismatch;
[error]  found   : Int(1)
[error]  required: (String, Int)
[error]   c2 += ("hh", 1)
[error]                ^
[error] two errors found
[error] (compile:compileIncremental) Compilation failed
[error] Total time: 3 s, completed Sep 1, 2016 1:22:52 AM

The pair I'm adding seems to be of the correct type as demanded by the HashMap. Why do I get a compile error?

Upvotes: 1

Views: 471

Answers (1)

badcook
badcook

Reputation: 3739

The += operator is overloaded to work with variadic arguments. Therefore when the compiler sees c2 += ("hh", 1) it interprets that as two arguments being passed in, one of which is "hh" and the other of which is 1. You can fix that either by using the -> operator, i.e. c2 += ("hh" -> 1) or enclosing the tuple in another series of parantheses, i.e. c2 += (("hh, 1)).

Slightly gory details below as requested in the comments.

As for how all this works, in the case of mutable collections such as HashMap, += is simply an ordinary method called with operator syntax (i.e. spaces instead of a .) or "infix notation" as the Scala community calls it, as any method in Scala can be. It is provided by the Growable trait which mutable collections mix in. You can see on the documentation for Growable both the single argument += method and the variadic method. In other words the following code would have also worked.

c2.+=(("hh", 1))

Not all +=s are created equal however. += commonly shows up in vars as well. Although it can be called with method syntax ., it's magic syntax sugar implemented directly by the Scala compiler. In particular any nonalphanumeric name followed by an = gets desugared. x $NAME= y becomes x = x.$NAME(y). In this case $NAME= is variadic if and only if $NAME is variadic.

var i = 0
i += 1
i.+=(1) // Also compiles

case class SuperInt(raw: Int) {
  def &*&(x: SuperInt) = SuperInt(raw + x.raw)
}
var x = SuperInt(1)
x &*&= SuperInt(1) // This works
x.&*&=(SuperInt(1)) // As does this
x &*&= (SuperInt(1), SuperInt(1)) // Does not compile because &*& is not variadic

Upvotes: 5

Related Questions