为什么使用模式匹配收集不能缩小特定 class?
Why collect with pattern match cannot narrow a specific class?
让我们考虑以下特征:
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()
编译器很高兴,但如果尝试 return 完全匹配:
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 编译器无法处理第二种情况?
行为在 Pattern Binders and Constructor Patterns 中指定。
在我看来是这样的:
模式的预期类型是(Int, AB)
(在这两种情况下)。
在case (id, a @ A(_))
中,A(_)
的预期类型是AB
,但实际类型是A
.
在case x @ (_, A(_))
中,x
的类型是模式(_, A(_))
的类型。它被实例化为 Tuple2[Int, AB]
before 查看 A(_)
内部,然后仅检查 A(_)
部分是否符合它:[= 中没有任何内容23=]
If the case class is polymorphic, then its type parameters are instantiated so that the instantiation of c conforms to the expected type of the pattern. The instantiated formal parameter types of c's primary constructor are then taken as the expected types of the component patterns p1,…,pn. The pattern matches all objects created from constructor invocations c(v1,…,vn) where each element pattern pi matches the corresponding value vi.
这将对 p1,...,pn 的实际类型做任何事情。
这似乎是因为模式匹配以自上而下的方式进入子模式,没有将任何附加信息从子模式传递到根模式。
何时
x @ (_, A(_))
被匹配,所有 x
所知道的模式是它具有 预期类型 (Int, AB)
,并且这成为 [=] 的推断类型11=].
但是当您将模式变量 i
和 a
附加到子模式时,可以提取更具体的信息并将其保存在 i
和 [=15= 的推断类型中].在这个带有 a @ A(_)
的特殊情况下,the following paragraph in the spec 似乎是相关的:
A pattern binder x@p
consists of a pattern variable x
and a pattern p
. The type of the variable x
is the static type T
of the pattern p
.
在 A(_)
的情况下,静态类型可以通过查看模式的顶级元素推断为 A
,而无需递归到子模式中。因此,a
的类型被推断为A
,id
的类型被推断为Int
,因此(id, a)
的类型被推断为(Int, A)
.
你的 tuple pattern case (_, _)
只是一个构造函数模式 case Tuple2(_, _)
.
具有类型参数 is inferred 的构造函数模式,例如 case _: Tuple2[x, y]
.
您的变量将具有推断出的类型,而您在问为什么 x
和 y
会推断出它们的类型。
在 the section on inference in typed patterns 中,它承诺使用暗示符合预期类型的最弱约束。
在这种情况下,最弱的约束是 <: Int
和 <: AB
。
另一方面,为了明确模式匹配的绑定变量类型如
(Vector.empty: Seq[Nothing]) match { case x @ Nil => x }
这不是类型测试而是相等性测试,在 2.12 而不是 2.13 上抛出,现在的规范 says:
The type of the variable x is the static type T implied by the pattern
p. A pattern p implies a type T if the pattern matches only values
of the type T.
这种语言可能表明您的直觉是正确的,因为显然 (_, A(_))
只匹配类型 (_, A)
的值。 the definition:
似乎证实了这一点
The pattern matches all objects created from constructor invocations
c(v1,…,vn) where each element pattern pi matches the corresponding
value vi.
然而,说模式隐含类型并不意味着绑定变量的类型是模式隐含的最窄类型。这对 Nil
示例意味着什么?
很遗憾,第一种形式是not yet legal syntax:
case x @ Tuple2[Int @unchecked, A @unchecked](_, A(_)) => x
case x @ Tuple2(_, A(_)) => x.asInstanceOf[(Int, A)]
让我们考虑以下特征:
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()
编译器很高兴,但如果尝试 return 完全匹配:
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 编译器无法处理第二种情况?
行为在 Pattern Binders and Constructor Patterns 中指定。
在我看来是这样的:
模式的预期类型是
(Int, AB)
(在这两种情况下)。在
case (id, a @ A(_))
中,A(_)
的预期类型是AB
,但实际类型是A
.在
case x @ (_, A(_))
中,x
的类型是模式(_, A(_))
的类型。它被实例化为Tuple2[Int, AB]
before 查看A(_)
内部,然后仅检查A(_)
部分是否符合它:[= 中没有任何内容23=]If the case class is polymorphic, then its type parameters are instantiated so that the instantiation of c conforms to the expected type of the pattern. The instantiated formal parameter types of c's primary constructor are then taken as the expected types of the component patterns p1,…,pn. The pattern matches all objects created from constructor invocations c(v1,…,vn) where each element pattern pi matches the corresponding value vi.
这将对 p1,...,pn 的实际类型做任何事情。
这似乎是因为模式匹配以自上而下的方式进入子模式,没有将任何附加信息从子模式传递到根模式。
何时
x @ (_, A(_))
被匹配,所有 x
所知道的模式是它具有 预期类型 (Int, AB)
,并且这成为 [=] 的推断类型11=].
但是当您将模式变量 i
和 a
附加到子模式时,可以提取更具体的信息并将其保存在 i
和 [=15= 的推断类型中].在这个带有 a @ A(_)
的特殊情况下,the following paragraph in the spec 似乎是相关的:
A pattern binder
x@p
consists of a pattern variablex
and a patternp
. The type of the variablex
is the static typeT
of the patternp
.
在 A(_)
的情况下,静态类型可以通过查看模式的顶级元素推断为 A
,而无需递归到子模式中。因此,a
的类型被推断为A
,id
的类型被推断为Int
,因此(id, a)
的类型被推断为(Int, A)
.
你的 tuple pattern case (_, _)
只是一个构造函数模式 case Tuple2(_, _)
.
具有类型参数 is inferred 的构造函数模式,例如 case _: Tuple2[x, y]
.
您的变量将具有推断出的类型,而您在问为什么 x
和 y
会推断出它们的类型。
在 the section on inference in typed patterns 中,它承诺使用暗示符合预期类型的最弱约束。
在这种情况下,最弱的约束是 <: Int
和 <: AB
。
另一方面,为了明确模式匹配的绑定变量类型如
(Vector.empty: Seq[Nothing]) match { case x @ Nil => x }
这不是类型测试而是相等性测试,在 2.12 而不是 2.13 上抛出,现在的规范 says:
The type of the variable x is the static type T implied by the pattern p. A pattern p implies a type T if the pattern matches only values of the type T.
这种语言可能表明您的直觉是正确的,因为显然 (_, A(_))
只匹配类型 (_, A)
的值。 the definition:
The pattern matches all objects created from constructor invocations c(v1,…,vn) where each element pattern pi matches the corresponding value vi.
然而,说模式隐含类型并不意味着绑定变量的类型是模式隐含的最窄类型。这对 Nil
示例意味着什么?
很遗憾,第一种形式是not yet legal syntax:
case x @ Tuple2[Int @unchecked, A @unchecked](_, A(_)) => x
case x @ Tuple2(_, A(_)) => x.asInstanceOf[(Int, A)]