So the scala compiler is complaining that a pattern match might not be exhaustive for the method foo
and I wonder why. This is the code:
abstract class Foo {
def foo(that: Foo): Unit = (this, that) match {
case (Foo_1(), Foo_1()) => //case 1
case (Foo_1(), Foo_2()) => //case 2
case (Foo_2(), Foo_1()) => //case 3
case (Foo_2(), Foo_2()) => //case 4
// Compiler warning
}
def fooThis(): Unit = this match {
case Foo_1() => //do something
case Foo_2() => //do something
// Works fine
}
def fooThat(that: Foo): Unit = that match {
case Foo_1() => //do something
case Foo_2() => //do something
// Works fine
}
}
case class Foo_1() extends Foo
case class Foo_2() extends Foo
And this is the error:
Warning:(5, 32) match may not be exhaustive.
It would fail on the following inputs: (Foo(), _), (Foo_1(), _), (Foo_2(), _), (_, Foo()), (_, Foo_1()), (_, Foo_2()), (_, _)
def foo(that: Foo): Unit = (this, that) match {
Since this
and that
are of type Foo
, and Foo
can only be of type Foo_1
or Foo_2
, the cases in foo
are all possible combinations.
I added fooThis
and fooThat
for sake of completeness and to show that matching Foo_1
and Foo_2
suffices. The compiler message suggests that there are other types that can be matched (i.e. Foo
and _
).
So why is this warning shown?
Related:
Welcome to Scala 2.12.1 (Java HotSpot(TM) Client VM, Java 1.8.0_131).
EDIT
The compiler seems to complain as soon as you use tuples. If we add a dummy variable to fooThis
as follows
def fooThis(): Unit = (this, Foo_1()) match {
case (Foo_1(),_) => //do something
case (Foo_2(),_) => //do something
}
we get the following compiler warning
Warning:(13, 27) match may not be exhaustive.
It would fail on the following input: (_, _)
def fooThis(): Unit = (this, Foo_1()) match {
The Scala compiler won't give exhaustive match warnings for non-sealed traits (like your Foo
). This explains why fooThis
and fooThat
compile without warnings.
If you want warnings here (and you should, because they're better than MatchError
exceptions at runtime) you have a couple of options:
Foo
sealed. This creates an ADT, which is safe to pattern match against in the sense that you'll get exhaustivity warnings when you forget a case. Option
is an ADT that you're probably familiar with from the standard library. Here, you've already got cases for both Foo_1
and Foo_2
, so you won't get an exhaustivity warning. But if you ever forget either case, you will. You probably want to make Foo_1
and Foo_2
final while you're at it.Foo
unsealed, use Typelevel Scala and enable its -Xlint:strict-unsealed-patmat
warnings.On the other hand, the Scala compiler will give exhaustive match warnings for final case classes like Tuple2
, which is what you're matching against in your foo
method.
To answer "why is the warning shown?", consider what happens if we do this:
case class Foo3() extends Foo
val foo3 = Foo3()
foo3.foo(foo3)
(Answer: it throws a MatchError
at runtime.)
The warning is the Scala compiler's way of helping you avoid the exception at runtime. If you want to make the warning go away, you could:
Foo
sealed (again, creating an ADT), preventing Foo3
from sneaking in elsewhere.case _ => ...
.((this, that): @unchecked) match { ...
.Don't do number 3, because it leaves you vulnerable to MatchError
s at runtime when someone introduces Foo3
.
So, perhaps the question isn't really "why does the match in foo
generate a warning", but "why doesn't the match in fooThis
and fooThat
generate a warning".
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With