Why does this throw MatchError?

The value of the below future is Failure(java.lang.RuntimeException) as expected, since RuntimeException is not handled:

    Future { throw new RuntimeException() }
      .recover { t =>
        {
          t match {
            case e: java.io.IOException => println("handling IOException")
          }
        }
      }

The code below just has a println added, but the value of the future is instead Failure(scala.MatchError):

    Future { throw new RuntimeException() }
      .recover { t =>
        {
          println("foo")
          t match {
            case e: java.io.IOException => println("handling IOException")
          }
        }
      }

Why does adding println cause this difference in behavior?

1 Like

Not sure offhand, but I don’t think the code is right to begin with. recover() takes a PartialFunction as its parameter; the match is an expression that embeds another PartialFunction, so it’s not doing what you expect.

My guess is that the println() is confusing things further, and leading to the system thinking that that’s a valid case and executing it. (But that’s just a guess.)

This expression really should be:

Future { throw new RuntimeException() }
    .recover {
      case e: java.io.IOException => println("handling IOException")
    }
2 Likes

I was surprised by something quite similiar not very long ago - see scala - Why using a variable makes a partial function less partial? - Stack Overflow and Expressions | Scala 3.4 :

When a PartialFunction is required, an additional member isDefinedAt is synthesized, which simply returns true. However, if the function literal has the shape x => x match { $...$ }, then isDefinedAt is derived from the pattern match in the following way: each case from the match expression evaluates to true, and if there is no default case, a default case is added that evaluates to false.

Without println it matches the special shape which allows compiler to convert the match into a partial function.

Maybe someone here could shed some light on why is this special shape handled by compiler at all? It is hard to see a situation where a pattern matching anonymous function could not be written instead. Perhaps there was some time in history when this enabled to do something?

3 Likes

Here is a Scala 2 ticket with some discussion.

The ticket is still open for an incidental bug, but touches on that canonical shape x => x match.

Note that since that original ticket, “regular function literals”, as well as “pattern matching anonymous functions”, compile to either Function or PartialFunction, as required.

Most wildfires are started by a careless throw MatchError.

1 Like

I suspect that by the time that that code reaches the component in the compiler that turns { a => b; c => d } into a partial function, it has already been desugared into something that is indistinguishable from x => x match { a => b; c => d }.

It seems I argued on that linked Scala 2 ticket that 8.5 of the spec says they are equivalent.

Another way of saying it is that a block of cases means nothing out of context.

SAM conversion came a year after my comment and makes it more obvious (to me) that my block of cases (or the equivalent function) is “implemented” or “realized” variously according to what is required.

It is not just a matter of “internal representation” by the compiler, but you can see how it is elaborated with

-Vprint:parser,typer,refchecks

which shows that Scala 2 constructs the partial function at typer but Scala 3 does it after typer.

I remember that the specifics do affect inference, from my attempts to improve it in Scala 2.