Magic PartialFunction in Scala

笑着哭i 提交于 2019-12-07 16:56:00

问题


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.


回答1:


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.




回答2:


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.




回答3:


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!