Hi all, I’ve stumbled upon a strange behavior regarding “nested” partial functions. I have the following code:
package example
object TestPF extends App {
val innerPf: PartialFunction[Any, Option[Unit]] = { case x: String => Some(()) }
val outerPf_1: PartialFunction[Any, Any] = { p =>
innerPf(p) match { case Some(_) => println("Something") }
}
val outerPf_2: PartialFunction[Any, Any] = { p =>
val something = p
// Only here compiler warning: "match may not be exhaustive. It would fail on the following input: None"
innerPf(something) match { case Some(_) => println("Something") }
}
println(s"innerPf.isDefinedAt(10): ${innerPf.isDefinedAt(10)}")
println(s"innerPf.isDefinedAt(\"abc\"): ${innerPf.isDefinedAt("abc")}")
try {
println(s"outerPf_1.isDefinedAt(10): ${outerPf_1.isDefinedAt(10)}")
} catch {
case _: MatchError => println("outerPf_1.isDefinedAt(10): MatchError")
}
try {
println(s"outerPf_1.isDefinedAt(\"abc\"): ${outerPf_1.isDefinedAt("abc")}")
} catch {
case _: MatchError => println("outerPf_1.isDefinedAt(\"abc\"): MatchError")
}
try {
println(s"outerPf_2.isDefinedAt(10): ${outerPf_2.isDefinedAt(10)}")
} catch {
case _: MatchError => println("outerPf_2.isDefinedAt(10): MatchError")
}
try {
println(s"outerPf_2.isDefinedAt(\"abc\"): ${outerPf_2.isDefinedAt("abc")}")
} catch {
case _: MatchError => println("outerPf_2.isDefinedAt(\"abc\"): MatchError")
}
// OUTPUT:
// innerPf.isDefinedAt(10): false
// innerPf.isDefinedAt("abc"): true
// outerPf_1.isDefinedAt(10): MatchError
// outerPf_1.isDefinedAt("abc"): true
// outerPf_2.isDefinedAt(10): true
// outerPf_2.isDefinedAt("abc"): true
}
As you can see I define three partial functions:
innerPfwhich is a simple partial function only defined on strings;outerPf_1which is a partial function that internally callsinnerPf. Notice that the body is just the call toinnerPfand a match statement, nothing else;outerPf_2which is similar toouterPf_1but has an additional instruction before the call toinnerPfand the match, i.e.val something = p
If you look at the output, there’s something odd. In particular, outerPf_1 and outerPf_2 behave slightly differently, even if I would expect them to be basically identical:
- of the two, I would say that
outerPf_2is the “normal” one, since it’s defined on all possible inputs (the fact that is will fail later on anything except strings is expected andisDefinedAtcannot know). In fact, the compiler even warns about the non exhaustive internal pattern match; outerPf_1, instead is a bit strange: the compiler gives no warning andisDefinedAtthrows a strangeMatchError, which I suppose should never be thrown by that function.
I tried that same exact code with scala 2.13.11, 3.3.3 and 3.5.1 and the outcome is exactly the same. Is this expected? Why are outerPf_1 and outerPf_2 behaving differently?