Scala pattern matching is not exhaustive for nested case classes

I have 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 a missing match:

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

But ProcessingError accepts in ProcessErrorType with only two extensions: InvalidFormat and EntityNotFound, which are taken into account in pattern matching. What am I missing?

Even more curious, if I change the type of the InvalidParameters or InvalidEndpoint parameter to String *, I will not get the error:

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

Any ideas?

+5
source share
4 answers

This is a confirmed error. I published an error report for this , and it was fixed for Scala 2.12.0-M4.

+4
source

Very interesting! Unfortunately, I did not find the answer. I revolved around http://www.scala-lang.org/files/archive/spec/2.11/08-pattern-matching.html#constructor-patterns , but I really did not find the right explanation for what was happening.

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 noticed that each of the following two modifications resolves the warning:
1) Change the signature of FooOne and FooTwo , so instead of accepting ClassOne and ClassTwo they take ClassOneImpl and ClassTwoImpl
2) Removing FooOne or FooTwo , so that there is only one case class extending Foo (which leads to only one case in comparison with the pattern).

Perhaps we could send a question and see what they say?

+1
source

You can help the compiler with untested annotation:

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

but you must be sure that your match is comprehensive.

0
source

I think completeness matching works on the same level of inheritance. RequestErrorType and ProcessingErrorType are part of the constructor where this comprehensive information is not checked.

You can see this from reading the code, but it seems the compiler does not.

0
source

Source: https://habr.com/ru/post/1238953/


All Articles