Question

I don't think this code should work, but it does (in Scala 2.10):

scala>     ((i: Int) => i.toString match {
     |        case s if s.length == 2 => "A two digit number"
     |        case s if s.length == 3 => "A three digit number"
     |     }): PartialFunction[Int,String]
res0: PartialFunction[Int,String] = <function1>

// other interactions omitted

scala> res0.orElse(PartialFunction((i: Int) => i.toString))
res5: PartialFunction[Int,String] = <function1>

scala> res5(1)
res6: String = 1

How does it work? I would expect a MatchError to be thrown inside res0.

The Scala language specification does not seem to explicitly document how res0 should be interpreted.

Was it helpful?

Solution

The trick is that the compiler is not interpreting your definition as a total function converted to a partial function -- it's actually creating a partial function in the first place. You can verify by noting that res0.isDefinedAt(1) == false.

If you actually convert a total function to a partial function, you will get the behavior you expected:

scala> PartialFunction((i: Int) => i.toString match {
     |       case s if s.length == 2 => "A two digit number"
     |       case s if s.length == 3 => "A three digit number"
     |     })
res0: PartialFunction[Int,String] = <function1>

scala> res0 orElse ({ case i => i.toString }: PartialFunction[Int, String])
res1: PartialFunction[Int,String] = <function1>

scala> res1(1)
scala.MatchError: 1 (of class java.lang.String)
// ...

In this example, PartialFunction.apply treats its argument as a total function, so any information about where it's defined is lost.

OTHER TIPS

orElse is defined on PartialFunction so that the argument is treated as a fallback for the cases when the original is not defined. See the API.

You say that if res0 does not match, you want to try your other pf instead. How this essentially works is:

if (res0.isDefinedAt(1)) {
  res0(1)
} else {
  other(1)
}

The orElse call creates an instance of OrElse, which inherits from PartialFunction: https://github.com/scala/scala/blob/master/src/library/scala/PartialFunction.scala#L159

When you now call apply on this OrElse, it will call f1.applyOrElse(x, f2): https://github.com/scala/scala/blob/master/src/library/scala/PartialFunction.scala#L162

This will call if (isDefinedAt(x)) apply(x) else f2(x): https://github.com/scala/scala/blob/master/src/library/scala/PartialFunction.scala#L117-L118

Therefore you will only get a MatchError, when neither of the pf's matches.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top