9

Set定义为Set[A]。它需要一个不变的参数。当我们传递协变参数时,执行以下操作按预期工作:

scala> val a = Set(new Object)
a: scala.collection.immutable.Set[Object] = Set(java.lang.Object@118c38f)

scala> val b = Set("hi")
b: scala.collection.immutable.Set[String] = Set(hi)

scala> a & b
<console>:10: error: type mismatch;
 found   : scala.collection.immutable.Set[String]
 required: scala.collection.GenSet[Object]
Note: String <: Object, but trait GenSet is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: Object`. (SLS 3.2.10)
              a & b

但以下工作:

scala> Set(new Object) & Set("hi")
res1: scala.collection.immutable.Set[Object] = Set()

如我所见,scala编译器转换Set("hi")Set[Object]类型并因此工作。

类型推断在这里做什么?有人可以链接到说明该行为的规范以及它一般何时发生吗?对于这种情况,它不应该引发编译时错误吗?作为相同操作类型的 2 个不同输出。

4

2 回答 2

7

不确定,但我认为您正在寻找的内容在“本地类型推断”下的语言规范中进行了描述(在撰写本文时,第 100 页的第 6.26.4 节)。

局部类型推断推断要传递给多态类型表达式的类型参数。假设e的类型为[ a 1 >: L 1 <: U 1 , ..., a n >: Ln <: U n ] T并且没有给出明确的类型参数。

本地类型推断将此表达式转换为类型应用程序e [ T 1 , ..., T n ]。类型参数T 1 , ..., T n的选择取决于表达式出现的上下文和预期的类型pt。有三种情况。

[ ... ]

如果表达式e显示为值而不应用于值参数,则类型参数是通过求解将表达式的类型T与预期类型pt相关联的约束系统来推断的。不失一般性,我们可以假设T是一个值类型;如果它是一种方法类型,我们应用 eta-expansion 将其转换为函数类型。求解意味着为类型参数a i找到类型T i的替换σ ,使得

  • 没有一个推断类型T i是单例类型

  • 尊重所有类型参数界限,即σ L i <: σ a iσ a i <: σ U i for i = 1, ..., n

  • 表达式的类型符合预期的类型,即σ T <: σ pt

如果不存在此类替换,则为编译时错误。如果存在多个替换,则局部类型推断将为每个类型变量a i选择解空间的最小或最大类型T i 。如果类型参数a i逆变出现在表达式的类型T中,则将选择最大类型T i 。在所有其他情况下,将选择最小类型T i ,即如果变量在类型T中出现协变、非变或根本不出现。我们称这种替换为类型T的给定约束系统的最优解。

简而言之:Scalac 必须为您省略的泛型类型选择值,并且它会在结果编译的约束下选择最具体的选择。

于 2013-08-03T06:23:48.873 回答
3

表达式Set("hi")可以是 ascala.collection.immutable.Set[String]或 a scala.collection.immutable.Set[Object],具体取决于上下文的要求。(当然,AString是有效Object的。)当你写这个时:

Set(new Object) & Set("hi")

上下文需要Set[Object],所以这就是推断的类型;但是当你写这个时:

val b = Set("hi")

上下文未指定,因此Set[String]选择了更具体的类型,然后(如您所料)会a & b导致类型错误。

于 2013-08-03T05:54:19.553 回答