【问题标题】:Scala pattern match is not exhaustive on nested case classesScala 模式匹配在嵌套案例类中并不详尽
【发布时间】:2015-12-22 13:16:15
【问题描述】:

我有一个案例类层次结构来编码一些请求和处理错误:

  sealed trait OpError
  sealed trait RequestErrorType
  sealed trait ProcessingErrorType

  final case class InvalidEndpoint(reason: String) extends RequestErrorType
  final case class InvalidParameters(reason: String) extends RequestErrorType

  final case class InvalidFormat(response: String) extends ProcessingErrorType
  final case class EntityNotFound(id: Long) extends ProcessingErrorType

  final case class RequestError(errorType: RequestErrorType) extends OpError
  final case class ProcessingError(errorType: ProcessingErrorType) extends OpError

如果我写一个跨越所有模式的简单匹配:

  def printMatches(error: OpError): Unit = error match {
    case RequestError(InvalidEndpoint(reason)) => //print something
    case RequestError(InvalidParameters(reason)) => //print something
    case ProcessingError(InvalidFormat(format)) => //print something
    case ProcessingError(EntityNotFound(entityId)) => //print something
  }

编译器给我一个关于缺少匹配的警告:

 match may not be exhaustive.
 It would fail on the following input: ProcessingError(_)
 def printMatches(error: OpError): Unit = error match {

但是 ProcessingError 接受一个只有两个扩展的 ProcessingErrorType:InvalidFormat 和 EntityNotFound,这两者都在模式匹配中被考虑在内。我错过了什么?

更奇怪的是,如果我将 InvalidParameters 或 InvalidEndpoint 的参数类型更改为 String*,我不会得到错误:

final case class InvalidParameters(reason: String*) extends RequestErrorType

有什么想法吗?

【问题讨论】:

  • printMatches(ProcessingError(new ProcessingErrorType{})) 不匹配
  • 这个例子违反了sealed合约。
  • 就 String* 参数的奇怪行为而言,当案例类具有可变参数参数时,听起来 Scala 会转向详尽检查:issues.scala-lang.org/browse/…

标签: scala pattern-matching


【解决方案1】:

这是一个已确认的错误。 I filed a bug report for this 此后已针对 Scala 2.12.0-M4 进行了修复。

【讨论】:

【解决方案2】:

非常有趣!不幸的是,我还没有找到答案。我一直在围绕http://www.scala-lang.org/files/archive/spec/2.11/08-pattern-matching.html#constructor-patterns 旋转,但我还没有真正找到对发生的事情的有效解释。

这是一个更简单的演示(希望你不介意):

sealed abstract class ClassOne
case class ClassOneImpl() extends ClassOne

sealed abstract class ClassTwo()
case class ClassTwoImpl() extends ClassTwo

sealed abstract class Foo
case class FooOne(x: ClassOne) extends Foo
case class FooTwo(x: ClassTwo) extends Foo

def printMatches(st: Foo): Unit = st match {
  case FooOne(ClassOneImpl()) => println()
  case FooTwo(ClassTwoImpl()) => println()
}

我观察到以下两个修改中的每一个都删除了警告:
1) 更改 FooOneFooTwo 签名,而不是采用 ClassOneClassTwo 他们采用 ClassOneImplClassTwoImpl
2) 删除FooOneFooTwo 以便只有一个case 类扩展Foo(这导致模式匹配中只有一种case)。

也许我们可以提交一个问题,看看他们怎么说?

【讨论】:

【解决方案3】:

您可以使用未经检查的注释帮助编译器:

... = (error: @unchecked) match ...

但你应该确定,你的匹配是详尽的。

【讨论】:

  • true,但我希望编译器帮助我找出无效实例,而不是反过来。 ;)
【解决方案4】:

我认为穷举匹配适用于单一继承级别。 RequestErrorTypeProcessingErrorType 是构造函数的一部分,其中不检查详尽性。

看代码可以看出来,但是编译器好像没有。

【讨论】:

  • 假设我从 printMatches 中注释掉 RequestError(InvalidParameters(reason)) 的匹配,然后编译器让我知道我错过了那个确切的变化:“匹配可能并不详尽。它会失败以下输入:RequestError(InvalidParameters(_))"。所以我猜编译器可以找出一些嵌套继承。
猜你喜欢
  • 2013-09-13
  • 2011-04-30
  • 1970-01-01
  • 2017-02-25
  • 1970-01-01
  • 1970-01-01
  • 2011-08-11
  • 2017-04-25
  • 1970-01-01
相关资源
最近更新 更多