Pattern matching error

As far as I know, the following pattern matching should work without any warnings or errors, but it is not :frowning:

def hasMoreThanTwo(input: Seq[Int]): Boolean = input match {
  case Seq() => false
  case _ +: Seq() => false
  case _ +: _ +: Seq() => false
  case _ +: tail =>
    println(s"size of the given input is ${tail.size + 1} ")
    true

  //    case _ =>
  //      println("is default case should be true or false? I am not expecting this case")
  //      false
}

getting following error

[error] ..../pattern-matching-error/src/main/scala/example/Test.scala:5:50: match may not be exhaustive.
[error] It would fail on the following input: (x: Seq[?] forSome x not in Nil)
[error]   def hasMoreThanTwo(input: Seq[Int]): Boolean = input match {
[error]                                                  ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed
[error] Total time: 2 s, completed 17 Apr 2023, 22:27:46

I feel it is bug, but I might be wrong as well.

Repo: GitHub - Seetaramayya/pattern-matching-error

so, you are right in that the following code is “safe”, in the sense that it will never fail with a MatchError.
However, you are wrong in thinking it should not warn.

The way the “match is not exhaustive” compiler warning work changed somewhere in the 2.13.x releases cycle.
Before, it warned if it knew the match was not exhaustive, but now it warns if it can’t prove it is exhaustive.
That may sound the same, but they are very different. Formally speaking, being unable to prove that something doesn’t satisfy a property is not the same as providing that such an object does not satisfy the property.
With match expressions, the compiler is able to reach one of three conclusions.
a. It knows it is exhaustive.
b. It knows it is not exhaustive. (I am not familiar with the compiler internals, but I guess this is actually the same as above)
c. It doesn’t know whether it is exhaustive or not.

And that third case is the important bit.
Previously, it would not warn, now it warns; which makes sense to many folks (like me) since better safe than sorry.


Now, your next question will be:

Why the compiler can’t know that the current match is not exhaustive?

Well, because for it to know that it must be able to solve the halting problem, and if the compiler would be able to do that Martin would be extremely rich.
Now, joke answer aside, the compiler only has two ways to know if a match is exhaustive.

  1. You are matching a sealed type; i.e. an ADT. This makes sense, the compiler knows exactly all the possible values it may take.
  2. You use case _, also known as cheating :stuck_out_tongue:

And, despite what it may look like, your code is not doing 1 and absolutely not 2.
Both Seq() & +: are custom extractors, not an ADT.


At this point (if you are not tired of my bad humor) you would be asking.

How to fix it then?

Two options.

What Luis would do:

Use a List instead of a Seq and pattern match using Nil & :: instead of Seq() & +:

What folks that like to use Seq would do:

Replace the case Seq() with case _ and put it as the last one.


PS: @SethTisue do you think this is worth it as a FAQ?
(of course, removing all the unnecessary failed attempt at comedy)

2 Likes

Hmmmm interesting…

I read your reply in the following way

For the given code, the compiler was working fine without any errors or warnings, lately due to the improvements in pattern matching area it started giving warnings (in our case those are promoted to errors)

Is my understanding correct?

Regarding this point:

Use a List instead of a Seq and pattern match using Nil & :: instead of Seq() & +:

I can not use List because I need generic data structure. I know cons operator works with the List I tried it immediately when I got the error.

IMHO: Too much of the information is also not a good idea.

I wouldn’t put it like that.
Technically speaking the compiler was not “working fine”, instead it was “less strict”.
But, I know this particular change was a bit controversial to many folks used to these custom extractors.
Anyways, you are correct that this code wouldn’t warm on previous versions of the compiler.

BTW, I forgot to mention in my original reply, that another solution is to use @nowarm


Yeah, I know some folks think like that.
I totally disagree but this is not the place for that discussion and is really just a matter of preference so whatever floats your boat :slight_smile:

Uhm, care to elaborate with that?
Not sure if you mean that List exposes too much information, or if you mean something of my original reply?