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.
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]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With