意外的Scala Map类型

时间:2013-01-19 17:16:46

标签: scala type-inference

在Scala(2.10)中,如果我要求List(1.0, 2),那么我会按预期获得List[Double]

但是......

如果我要求Map(1.0 -> 'A', 2 -> 'B'),我会获得Map[AnyVal, Char]。我希望密钥属于Double类型。询问Map[Double, Char](1.0 -> 'A', 2 -> 'B)会在'2'上出现类型不匹配。

这让我觉得最让人困惑!是不是不一致?

旁白: List[(Double, Char)]((1.0, 'A'), (2, 'B')).toMap虽然为我提供了地图[Double,Char]。

2 个答案:

答案 0 :(得分:6)

如果你研究Scala的type hierarchy,你会发现原始类型不属于子类型/超类型关系。但是,有一种称为数字扩展的机制,例如,允许您通过传入Double来调用带有Int参数的方法。然后Int会自动“扩展”为Double

这就是List(1.0, 2)为您提供List[Double]的原因。

但是Map构造函数需要Tuple[A, B]个参数。数字扩展不适用于高阶类型,因此如果混合数字类型,目标类型推断不适合您。

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

此外,箭头操作符->妨碍了您:

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

这是我认为的类型推断的另一个限制。编写元组a -> b(a, b)的语法糖,由Predef上的隐式方法any2ArrowAssoc提供。没有这种间接,如果直接构造Tuple2,它就可以工作:

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

所以数字扩展仍然不起作用,但至少你可以强制执行类型:

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

显示数字扩展工作的最后一个示例:

def map[A, B](keys: A*)(values: B*) = Map((keys zip values): _*)
map(1.0, 2)('A', 'B') // Map[Double, Char]

答案 1 :(得分:3)

对于List,没有类型声明,Scala查看所有元素并尝试查找公共类型。在您的情况下,由于Int可以转换为Double,它会将您的混合List转换为List [Double],以促进您的Int。

Map构造函数采用一系列2元组。如果你刚构建了一个元组列表,你会得到相同的行为:

scala> List((1, "one"), (2.0, "two.oh"))
res0: List[(AnyVal, String)] = List((1,one), (2.0,two.oh))

Tuple2 [Int,String]无法自动提升为Tuple2 [Double,String]。在这种情况下,您需要使用类型声明帮助编译器:

scala> val x: List[(Double, String)] = List((1, "one"), (2.0, "two.oh"))
x: List[(Double, String)] = List((1.0,one), (2.0,two.oh))

scala> val x = List[(Double, String)]((1, "one"), (2.0, "two.oh"))
x: List[(Double, String)] = List((1.0,one), (2.0,two.oh))

或在你的情况下:

scala> val x = List[(Double, String)]((1, "one"), (2.0, "two.oh")).toMap
x: scala.collection.immutable.Map[Double,String] = Map(1.0 -> one, 2.0 -> two.oh)

出于某种原因,在Map上使用类型声明不起作用。不确定原因:

scala> val x = Map[Double, String](1 -> "one", 2.0 -> "two.oh")
<console>:7: error: type mismatch;
found   : (Int, String)
required: (Double, String)
   val x = Map[Double, String](1 -> "one", 2.0 -> "two.oh")