似乎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]]
答案 0 :(得分:2)
这里没有CanBuildFrom
个工作,只有Map
个密钥的不变性。
当你写:
Map("foo" -> Map("bar" -> 6), "baz" -> Map.empty)
编译器尝试推断出它的类型,并且这样做需要找出Map("bar" -> 6)
和Map.empty
的常见类型 - 最小上限
Map.empty
为Map[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])