Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding to scala map within for loop and conditional statement

Tags:

scala

I'm getting an error message of "error: type arguments [Any] do not conform to trait Cloneable's type parameter bounds [+A <: AnyRef]," which I can't make heads or tails of.

Specifically,

var M = mutable.Map[Int, mutable.Set[Int]]()
for(i <- 1 to 100; j <- 1 to 100) {
    if(!M.contains(i)) {M += i -> mutable.Set[Int](j)}
    else {M(i) += j} 
}

(I'm actually trying something more complicated, but this is the error generating code tweaked and simplified to a minimum)

And the last line of the above code generates the error message. If I strip it down further

for(i <- 1 to 100; j <- 1 to 100) {
    if(!M.contains(i)) {M += i -> mutable.Set[Int](j)}
}

it works!

How do I make the above code work?

like image 856
JasonMond Avatar asked Aug 23 '11 00:08

JasonMond


People also ask

How do you add a value to a map in Scala?

Solution. Add elements to a mutable map by simply assigning them, or with the += method. Remove elements with -= or --= . Update elements by reassigning them.

Does Scala map preserve order?

ListMap implements an immutable map using a list-based data structure, and thus preserves insertion order.

What is the syntax of creating map in Scala?

Another way of creating an empty Map is by using the apply method: val emptyMap: Map[Int, String] = Map[Int, String].apply() Scala has a special syntactic sugar for this which gives us the ability to call it using only the parentheses: val emptyMap: Map[Int, String] = Map[Int, String]()


2 Answers

Digal diagnosed the problem (failure to unify the types of the if-else branches) and it looks like a compiler bug. Here's a further simplified case that will give an error in the REPL, after a lengthy compilation time,

if (true) {
  null: collection.mutable.Map[Int, Int]
} else {
  null: collection.mutable.Set[Int]
}

In the meantime, you can get your code to compile with an explicit type sprinkled somewhere in the if-else statement,

for(i <- 1 to 100; j <- 1 to 100) {
  if(!M.contains(i)) {M += i -> mutable.Set[Int](j)}
  else {M(i) += j}: Unit 
}

I filed an issue here: https://issues.scala-lang.org/browse/SI-4938

like image 52
Kipton Barros Avatar answered Nov 15 '22 08:11

Kipton Barros


I've reduced your example even further:

scala> if(!M.contains(1)) {M += 1 -> mutable.Set[Int](1)} else {M(1) += 1}; 
<console>:9: error: type arguments [Any] do not conform to trait Cloneable's type parameter bounds [+A <: AnyRef]
val res17 =
    ^

The problem seem to occur when compiler tries to find common return type for both branches: The first one is

scala> M += 1 -> mutable.Set[Int](1)
res19: scala.collection.mutable.Map[Int,scala.collection.mutable.Set[Int]] = ...

And the "else" part is

scala> M(1) += 1
res18: scala.collection.mutable.Set[Int] = Set(1)

If I add a return value to the end of this expression, REPL eats it without errors:

scala> if(!M.contains(1)) {M += 1 -> mutable.Set[Int](1)} else {M(1) += 1}; println("hello")
hello

Because the expression's return type is Unit.

like image 43
Digal Avatar answered Nov 15 '22 06:11

Digal