为什么用模式匹配收集不能缩小特定类?

时间:2018-05-27 10:43:51

标签: scala pattern-matching type-inference scala-collections scala-compiler

让我们考虑一下特质:

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编译器无法处理第二种情况?

3 个答案:

答案 0 :(得分:5)

这似乎是因为模式匹配以自上而下的方式进入子模式,而不会将子模式中的任何其他信息传递给根模式。

x @ (_, A(_))
匹配

,所有x都知道该模式是否具有期望类型 (Int, AB),这将成为x的推断类型。< / p>

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

  

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

A(_)的情况下,仅通过查看模式的顶级元素就可以推断静态类型为A,而不会递归到子模式中。因此,a的类型被推断为Aid的类型被推断为Int,因此(id, a)被推断为类型{ {1}}。

答案 1 :(得分:3)

行为在Pattern BindersConstructor Patterns中指定。

我认为这样:

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

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

  3. case x @ (_, A(_))中,x的类型是模式(_, A(_))的类型。在内部查看Tuple2[Int, AB]之前,它已被A(_) 实例化,然后只检查A(_)部分是否符合它:

    中没有任何内容
      

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

    可以对p1,...,pn。

  4. 的实际类型做任何事情

答案 2 :(得分:2)

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

类型参数为is inferred的构造函数模式,如case _: Tuple2[x, y]

您的变量将具有推断的类型,并且您问为什么xy推断出它们的方式。

the section on inference in typed patterns中,它承诺使用最弱的约束条件来暗示与预期类型的​​一致性。

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

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

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

这不是一个类型测试,而是一个相等的测试,并抛出2.12而不是2.13,现在规范says

  

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

这种语言可能表明您的直觉是正确的,因为显然(_, A(_))仅匹配(_, A)类型的值。这似乎由the definition确认:

  

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

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

不幸的是,第一种形式是not yet legal syntax

case x @ Tuple2[Int @unchecked, A @unchecked](_, A(_)) => x 
case x @ Tuple2(_, A(_)) => x.asInstanceOf[(Int, A)]