Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala pattern match is not exhaustive on nested case classes

I've got a case class hierarchy to encode some request and processing errors:

  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

If I write a simple match across all patterns:

  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
  }

the compiler gives me a warning about missing match:

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

But ProcessingError takes in a ProcessingErrorType with only two extensions: InvalidFormat and EntityNotFound, both which are accounted for in the pattern match. What am I missing?

Even more curious is that if I change the parameter type of InvalidParameters or InvalidEndpoint to a String*, I don't get the error:

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

Any ideas?

like image 635
ssanj Avatar asked Dec 22 '15 13:12

ssanj


2 Answers

This is a confirmed bug. I filed a bug report for this and it has been since fixed for Scala 2.12.0-M4.

like image 89
ssanj Avatar answered Oct 03 '22 20:10

ssanj


Very interesting! Unfortunately, I haven't found an answer. I've been revolving around http://www.scala-lang.org/files/archive/spec/2.11/08-pattern-matching.html#constructor-patterns but I haven't really found a valid explanation for what's going on.

Here's a simpler demo (hope you don't mind):

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()
}

I've observed that each of the following two modifications removes the warning:
1) Changing FooOne and FooTwo signatures so that instead of taking ClassOne and ClassTwo they take ClassOneImpl and ClassTwoImpl
2) Removing FooOne or FooTwo so that there's only one case class extending Foo (which leads to only one case in pattern matching).

Perhaps we could submit an issue and see what they say?

like image 43
slouc Avatar answered Oct 03 '22 19:10

slouc