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.
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.
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.
来源:https://stackoverflow.com/questions/19973026/magic-partialfunction-in-scala