Hi there,
I’m trying to migrate some of our company’s projects to Scala 2.13.4 and I’m hitting some problems with the new exhaustivity checking algorithm. While this is obviously a very good change, it becomes a problem in some cases where the compiler cannot infer that a type is effectively sealed.
For example, we are using Opt
- a value class version of Option
. Its usage looks like this:
val someString: Opt[String] = Opt("foo")
val noneString: Opt[String] = Opt.Empty
someString match {
case Opt(str) => println(s"some: $str")
case Opt.Empty => println("none")
}
Unfortunately, the compiler doesn’t know that Opt.apply
and Opt.Empty
are the only possible cases and thinks that this pattern match is non exhaustive. Since Opt
is an unsealed type (as seen by the compiler), I can work around that problem by opting out of strict-unsealed-patmat
.
However, the problem resurfaces when Opt
is wrapped into a sealed type, e.g. Try
:
val tryOpt: Try[Opt[String]] = Success(Opt("foo"))
tryOpt match {
case Success(Opt(str)) => println(s"some $str")
case Success(Opt.Empty) => println("none")
case Failure(cause) => cause.printStackTrace()
}
Even with the strict-unsealed-patmat
turned off, the compiler complains that:
match may not be exhaustive.
It would fail on the following input: Success((x: com.avsystem.commons.misc.Opt[?] forSome x not in Empty))
Another example is with our enum implementation, ValueEnum
:
final class Weekday(implicit enumCtx: EnumCtx) extends AbstractValueEnum
object Weekday extends AbstractValueEnumCompanion[Weekday] {
final val Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday: Value = new Weekday
}
val tryWeekday: Try[Weekday] = Success(Weekday.Monday)
tryWeekday match {
case Success(Weekday.Monday | Weekday.Tuesday | Weekday.Wednesday) => println("first half")
case Success(Weekday.Thursday) => println("middle")
case Success(Weekday.Friday | Weekday.Saturday | Weekday.Sunday) => println("second half")
case Failure(cause) => cause.printStackTrace()
}
The compiler has no idea that Monday
, Tuesday
, etc. are all possible values for Weekday
- this is guaranteed by the ValueEnum
machinery. So this time I get this warning:
match may not be exhaustive.
It would fail on the following input: Success((x: Weekday forSome x not in (Friday, Monday, Saturday, Sunday, Thursday, Tuesday, Wednesday)))
What are the possible solutions? There are some that come to my mind:
- Opting out of
strict-unsealed-patmat
should also work when the unsealed type is wrapped in a sealed type (likeTry[Weekday]
) - There should be a way to explicitly exempt some types from exhaustivity checking, e.g. with an annotation.
- There should be a way to tell the compiler about possible values and constructors of an effectively sealed type. Even if this was as low level as some API available to compiler plugins, I would be happy.
The no. 3 option would be absolutely the best because this would allow full integration of types like Opt
into the new exhaustivity checking algorithm.