1

Suppose we have a trait with 3 subcase classes. when we match this trait with two of them and forgot to check the third one, probably, we do not notice it during compile-time and then we get a runtime error especially in the case of using this pattern match in a mutually recursive function.

Is there any Language construct (kind of specific pattern match perhaps) that can assure all subclasses are handled? If so what about more sophisticated pattern matches? (more complex than just matching subclasses of a trait) Is there any mechanism that can make us assure those pattern matches are total function?

Matthias Berndt
  • 4,387
  • 1
  • 11
  • 25

2 Answers2

5

This is not possible in the general case. In a language with dynamic code loading, Class Hierarchy Analysis is equivalent to solving the Halting Problem. Therefore, it is impossible for the compiler to know in the general case how many subclasses the trait has.

You could add or remove subclasses at runtime, also you could add or remove subclasses to the subclasses of the trait, and so on.

It is, however, possible to do this for sealed traits whose subclasses are all also at least sealed or final (thus effectively "turning off" the capability of loading new subclasses at runtime). In that case, Scala will optimize the pattern match to a more efficient representation, and you will get a warning for a non-exhaustive pattern match.

Now all you need to do is to make sure to either read the warning, or tell the compiler to treat warnings as fatal errors (-Xfatal-warnings).


[Technically, the JVM does not know about sealed, so you can actually still load new subclasses using Java Reflection. However, this means you are leaving the realms of Scala, and you are on your own: the Scala compiler cannot protect you from things you do outside of Scala.]

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
3

Answered here: What is a sealed trait?

You need to use sealed trait and treat compiler warnings as errors to ensure that match is exhaustive.

sealed trait Base
final class A extends Base
final class B extends Base

val data: Base = new B()

// you get compiler warning here that match might not be exhaustive
// add -Xfatal-warnings to fail compilation on warnings: 
// https://docs.scala-lang.org/overviews/compiler-options/index.html
data match { 
  case _: B => println("B")
}
fenixil
  • 2,106
  • 7
  • 13