Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexpected Scala Map type

In Scala (2.10) if I ask for a List(1.0, 2) then I get a List[Double], as expected.

But...

if I ask for a Map(1.0 -> 'A', 2 -> 'B') I get a Map[AnyVal, Char]. I'd like the keys to be of type Double. Asking for Map[Double, Char](1.0 -> 'A', 2 -> 'B) gives a type mismatch on '2'.

This I find most confusing! Is it not inconsistent?

Aside: List[(Double, Char)]((1.0, 'A'), (2, 'B')).toMap gives me a Map[Double, Char] though.

like image 879
Pengin Avatar asked Jan 19 '13 17:01

Pengin


1 Answers

If you look into Scala's type hierarchy, you can see that the primitive types are not in a sub-type/super-type relationship. However, there is a mechanism called numeric widening which for example allows you to call a method that takes a Double argument, by passing in say an Int. The Int then is automatically "widened" to Double.

This is the reason that List(1.0, 2) gives you List[Double].

But the Map constructor takes Tuple[A, B] arguments. The numeric widening doesn't apply to higher order types, so the target type inference doesn't work for you if you mix numeric types.

case class Test[A](tup: (A, Char)*)
Test(1.0 -> 'A', 2 -> 'B') // AnyVal

Moreover, the arrow operator -> gets in your way:

Test[Double](2 -> 'B') // found: (Int, Char)  required: (Double, Char)

This is another limitation of the type inference I think. Writing a tuple a -> b is syntactic sugar for (a, b), provided by the implicit method any2ArrowAssoc on Predef. Without this indirection, if you construct the Tuple2 directly, it works:

Test[Double]((2, 'B'))

So the numeric widening still doesn't work, but at least you can enforce the type:

Map[Double, Char]((1.0, 'A'), (2, 'B'))

A final example showing numeric widening working:

def map[A, B](keys: A*)(values: B*) = Map((keys zip values): _*)
map(1.0, 2)('A', 'B') // Map[Double, Char]
like image 67
0__ Avatar answered Oct 22 '22 17:10

0__