Match would fail on the following input: List(_)

I got an interesting compiler warning on the code below.


418[info] Compiling 10 Scala sources to /builds/jnewton/earthmap/globe/target/scala-2.13/test-classes ...
419[warn] /builds/jnewton/earthmap/globe/src/test/scala/enhancements/EnhancementTest4.scala:93:7: match may not be exhaustive.
420[warn] It would fail on the following input: List(_)
421[warn]       sorted match {
422[warn]       ^
423[warn] one warning found

Here is the offending code. It looks to me like there is a case for List(_). Does this warning make sense? Maybe there’s something obvious that I don’t see?

    @tailrec
    def check(sorted:List[City]):Unit = {
      sorted match {
        case List() => ()
        case List(_) => ()
        case c1::c2::cs if c1.loc.lat == c2.loc.lat =>
          assert(c1.loc.lon <= c2.loc.lon, s"cities $c1 and $c2 not sorted west to east")
          check(c1::cs)
        case c1::c2::cs =>
          assert(c1.loc.lat >= c2.loc.lat, s"cities $c1 and $c2 not sorted north to south")
          check(c2::cs)
      }
    }

I can think of many ways to fix the code to avoid the warning. But I’m just surprised that the warning is emitted in the first place.

Looks like a bug to me, and seems to be a regression - I can reproduce this warning with 2.13.5 but not with 2.13.2.

I remember some discussion about this, I think from dwijnand somewhere on Github, that this is hard to get right.

Of course, you could restructure your code:

def check(sorted:List[City]):Unit = {
      sorted match {
        case c1::c2::cs if c1.loc.lat == c2.loc.lat =>
          assert(c1.loc.lon <= c2.loc.lon, s"cities $c1 and $c2 not sorted west to east")
          check(c1::cs)
        case c1::c2::cs =>
          assert(c1.loc.lat >= c2.loc.lat, s"cities $c1 and $c2 not sorted north to south")
          check(c2::cs)
        case _ =>
      }
    }

Oh, and maybe a non-recursive solution might be more readable:

sorted.sliding(2).foreach {
  case (c1, c2) => ...
}

If you want to go down that rabbit hole (of chasing the compiler issue), start here: Check exhaustivity of pattern matches with "if" guards and custom extractors (by default) and of unsealed types (by opt-in). by dwijnand · Pull Request #9140 · scala/scala · GitHub

1 Like

With this catchall you’re basically waiving the exhaustiveness check altogether, though.

It seems to be sufficient to replace List(_) with _ :: Nil.

2 Likes

Here’s a relevant issue from @martijnhoekstra :smile: Complementing irrefutable unapplySeq aren't recognized as exhaustive · Issue #12252 · scala/bug · GitHub

So, you could also workaround using:

sorted match {
    case Nil | _ :: Nil =>
    ...
}

Um, that’s just what @sangamon said, too… :bowing_man: