6

让我们考虑以下特征:

sealed trait AB
case class A(a: Int) extends AB
case class B(b: Int) extends AB

我试图将collect集合限制为特定的子类。

如果我尝试collect匹配单个组件并重新组装元组:

scala> Seq.empty[(Int, AB)].collect { case (id, a @ A(_))  => (id, a) } : Seq[(Int, A)]
res6: Seq[(Int, ab.A)] = List()

编译器很高兴,但如果尝试返回完全匹配:

scala> Seq.empty[(Int, AB)].collect { case x @ (_, A(_))  => x } : Seq[(Int, A)]

事情变得丑陋:

<console>:27: error: type mismatch;
 found   : Seq[(Int, ab.AB)]
 required: Seq[(Int, ab.A)]
       Seq.empty[(Int, AB)].collect { case x @ (_, A(_))  => x } : Seq[(Int, A)]

为什么 Scala 编译器无法处理第二种情况?

4

3 回答 3

6

这似乎是因为模式匹配以自上而下的方式进入子模式,而没有将任何附加信息从子模式传递到根模式。

什么时候

x @ (_, A(_))

匹配,所有x关于模式的知识是它具有预期的类型 (Int, AB),这成为推断的类型x

但是,当您将模式变量i和附加a到子模式时,可以提取更具体的信息并将其保存在 和 的推断类型ia。在这种特殊情况下a @ A(_)规范中的以下段落似乎相关:

模式绑定器x@p由模式变量x和模式组成p。变量的类型是模式x的静态类型。Tp

在 的情况下A(_),可以A仅通过查看模式的顶级元素来推断静态类型,而无需递归到子模式。因此,a推断出的类型是A,推断出的类型idInt,因此(id, a)推断出有类型(Int, A)

于 2018-05-27T13:42:36.293 回答
4

行为在Pattern BindersConstructor Patterns中指定。

在我看来是这样的:

  1. 模式的预期类型是(Int, AB)(在这两种情况下)。

  2. case (id, a @ A(_))中,预期类型A(_)AB,但实际类型是A

  3. 其中case x @ (_, A(_))x's type 是模式的类型(_, A(_))。在查看内部Tuple2[Int, AB] 之前将其实例化为A(_),然后A(_)仅检查该部分以符合它:在

    如果 case 类是多态的,则实例化其类型参数,以便 c 的实例化符合模式的预期类型。然后将 c 的主构造函数的实例化形式参数类型作为组件模式 p1,…,pn 的预期类型。该模式匹配从构造函数调用 c(v1,…,vn) 创建的所有对象,其中每个元素模式 pi 匹配相应的值 vi。

    它将对 p1,…,pn 的实际类型做任何事情。

于 2018-05-27T13:23:50.753 回答
3

您的元组模式 case (_, _)只是一个构造函数模式case Tuple2(_, _)

带有类型参数的构造函数模式被推断case _: Tuple2[x, y].

您的变量将具有推断的类型,并且您要问为什么是x并且y推断它们的方式。

类型化模式中的推理部分,它承诺使用暗示符合预期类型的​​最弱约束。

在这种情况下,最弱的约束是<: Int<: AB

另一方面,为了阐明模式匹配的绑定变量的类型,例如

(Vector.empty: Seq[Nothing]) match { case x @ Nil => x }

这不是类型测试,而是平等测试,并且抛出 2.12 但不是 2.13,规范现在

变量 x 的类型是模式 p 所隐含的静态类型 T。如果模式只匹配类型 T 的值,则模式 p暗示类型 T。

这种语言可能表明您的直觉是正确的,因为显然(_, A(_))只匹配 type 的值(_, A)定义似乎证实了这一点:

该模式匹配从构造函数调用 c(v1,…,vn) 创建的所有对象,其中每个元素模式 pi 匹配相应的值 vi。

然而,说模式隐含一个类型并不意味着绑定变量的类型是模式隐含的最窄类型。这对这个Nil例子意味着什么?

不幸的是,第一种形式还不是合法的语法

case x @ Tuple2[Int @unchecked, A @unchecked](_, A(_)) => x 
case x @ Tuple2(_, A(_)) => x.asInstanceOf[(Int, A)]
于 2018-06-01T07:17:32.597 回答