在Scala中键入Map的推断

时间:2017-03-07 03:03:40

标签: scala

似乎Scala Set / Map可能导致有线类型推断。

例如,在REPL中

$ val m2 = Map("foo" -> Map("bar" -> 6), "baz" -> Map.empty)
> scala.collection.immutable.Map[String,scala.collection.immutable.Map[_ <: String, Int]] = Map(foo -> Map(bar -> 6), baz -> Map())

我知道某些CanBuildFrom和“默认值”可能会导致immutable.Map类型。

但是为什么其中...Map[_ <: String, Int]而不是...Map[String, Int]

观察

下面定义的

m1按预期推断。

$ val m1 = Map("foo" -> Map("bar" -> 5))
> scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,Int]] = Map(foo -> Map(bar -> 5))

通过显式类型归属,两者都可以“正确”转换(我认为这是由于CanBuildFrom):

val _m1: Map[String, Map[String, Int]] = Map("foo" -> Map("bar" -> 5))
val _m2: Map[String, Map[String, Int]] = Map("foo" -> Map("bar" -> 6), "baz" -> Map.empty)

我关心这一点,因为当我使用一些函数库时,例如cats,我必须确保两个操作数都是相同的类型,我想知道何时我应该使用类型归属。例如,

import cats.Semigroup
import cats.implicits._
m1 ++ m2 // works
m1 |+| m2 // error
// found   : scala.collection.immutable.Map[String,scala.collection.immutable.Map[_ <: String, Int]]
// required: scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,Int]]

1 个答案:

答案 0 :(得分:2)

这里没有CanBuildFrom个工作,只有Map个密钥的不变性。

当你写:

Map("foo" -> Map("bar" -> 6), "baz" -> Map.empty)

编译器尝试推断出它的类型,并且这样做需要找出Map("bar" -> 6)Map.empty的常见类型 - 最小上限

如果您不提供类型参数,则

Map.emptyMap[Nothing, Nothing],因此编译器会计算Map[String, Int]Map[Nothing, Nothing]的最小上限,并且它会出现存在类型 Map[_ <: String, Int]。这是因为Map对其第一个类型参数不变。 (Map[Nothing, Int]不是Map[String, Int]

当您添加类型注释时,它会起作用,因为您正在添加一个提示,编译器使用该提示来推断Map.empty的类型参数。如果没有提示,它会愉快地使用Map[Nothing, Nothing]。这是类型推断的轻微缺陷吗?也许。我总是喜欢提供CollectionType.empty的类型参数,只是为了安全。

val m2 = Map("foo" -> Map("bar" -> 6), "baz" -> Map.empty[String, Int])