在 match case 中使用模式联编程序进行类型推断无法按预期工作
Type inference with pattern binder at sing @ in match case doesn't work as expected
假设 Lofty
是一个密封特征,Earthy
是它的一种情况 类。在这样的比赛中:
loftyItem match {
...
case e @ Earthy(x,y,z) => { foo(e) }
...
}
其中 foo
需要一个 Earthy
作为参数,编译器崩溃了,因为 e
仅被推断为 Lofty
类型。我可以解决这个问题,但它不符合我认为事情应该如何发展的模型。 (我正在使用 Scala 2.13.5。)有人可以解释为什么编译器的行为有意义,让我再次对 Scala 感到满意吗?
回复评论,我说得更准确一点:
object QTest {
trait Ethereal
case class Lofty[A <: Ethereal](val slotMaybe: Option[A]) {
}
class Earthy(val field: Int) extends Ethereal
object Earthy {
def apply(fx: Int): Earthy = {
new Earthy(fx)
}
def unapply(x: Ethereal): Option[Int] = x match {
case y: Earthy => Some(y.field)
case _ => None
}
}
def testReally(just: Lofty[Ethereal]):
Lofty[Earthy] = {
just.slotMaybe match {
case Some(c) => c match {
case cnfC @ Earthy(i) => {
just.copy(slotMaybe = Some(cnfC))
}
case _ => throw new RuntimeException("oops")
}
case _ => throw new RuntimeException("oops")
}
}
}
编译产生错误:
QTest.scala:25: error: type mismatch;
found : QTest.Ethereal
required: QTest.Earthy
just.copy(slotMaybe = Some(cnfC))
我显然是仓促下了结论,但完整的例子似乎也有同样的问题。为什么编译器为 cnfC
而不是 Earthy
推断类型 Ethereal
?即使编译器为 @
的大多数用途获得了正确的类型,为什么它在这里出错了?
A pattern p
implies a type T
if the pattern matches only values of the type T
.
中的模式 Earthy(i)
case cnfC @ Earthy(i) =>
代表extractor pattern意思是会根据你定义的unapply
匹配,也就是
object Earthy {
def unapply(x: Ethereal): Option[Int] = ???
}
因为 x
的声明类型更宽 Ethereal
而不是更窄 Earthy
它不会匹配
... only values of the type T
where T = Earthy
,但它也可以匹配 Ethereal
的其他子类型。因此编译器只能确定它将是 some Ethereal
.
如果你想让它用提取器模式编译,那么要么将你的 unapply
声明为
object Earthy {
def unapply(x: Earthy): Option[Int] = ???
}
或更好的是使用案例 class 而不是自动获得正确的 unapply
case class Earthy(field: Int) extends Ethereal
假设 Lofty
是一个密封特征,Earthy
是它的一种情况 类。在这样的比赛中:
loftyItem match {
...
case e @ Earthy(x,y,z) => { foo(e) }
...
}
其中 foo
需要一个 Earthy
作为参数,编译器崩溃了,因为 e
仅被推断为 Lofty
类型。我可以解决这个问题,但它不符合我认为事情应该如何发展的模型。 (我正在使用 Scala 2.13.5。)有人可以解释为什么编译器的行为有意义,让我再次对 Scala 感到满意吗?
回复评论,我说得更准确一点:
object QTest {
trait Ethereal
case class Lofty[A <: Ethereal](val slotMaybe: Option[A]) {
}
class Earthy(val field: Int) extends Ethereal
object Earthy {
def apply(fx: Int): Earthy = {
new Earthy(fx)
}
def unapply(x: Ethereal): Option[Int] = x match {
case y: Earthy => Some(y.field)
case _ => None
}
}
def testReally(just: Lofty[Ethereal]):
Lofty[Earthy] = {
just.slotMaybe match {
case Some(c) => c match {
case cnfC @ Earthy(i) => {
just.copy(slotMaybe = Some(cnfC))
}
case _ => throw new RuntimeException("oops")
}
case _ => throw new RuntimeException("oops")
}
}
}
编译产生错误:
QTest.scala:25: error: type mismatch;
found : QTest.Ethereal
required: QTest.Earthy
just.copy(slotMaybe = Some(cnfC))
我显然是仓促下了结论,但完整的例子似乎也有同样的问题。为什么编译器为 cnfC
而不是 Earthy
推断类型 Ethereal
?即使编译器为 @
的大多数用途获得了正确的类型,为什么它在这里出错了?
中的模式A pattern
p
implies a typeT
if the pattern matches only values of the typeT
.
Earthy(i)
case cnfC @ Earthy(i) =>
代表extractor pattern意思是会根据你定义的unapply
匹配,也就是
object Earthy {
def unapply(x: Ethereal): Option[Int] = ???
}
因为 x
的声明类型更宽 Ethereal
而不是更窄 Earthy
它不会匹配
... only values of the type
T
where T = Earthy
,但它也可以匹配 Ethereal
的其他子类型。因此编译器只能确定它将是 some Ethereal
.
如果你想让它用提取器模式编译,那么要么将你的 unapply
声明为
object Earthy {
def unapply(x: Earthy): Option[Int] = ???
}
或更好的是使用案例 class 而不是自动获得正确的 unapply
case class Earthy(field: Int) extends Ethereal