示例 1:当组合中的至少一个函数是部分函数时,返回总函数的原因是什么?
这是因为第一个示例中的第一个函数是一个总函数 (Function1),其 andThen 方法无论第二个函数是总函数还是部分函数都返回一个 Function1:
def andThen[A](g: (R) => A): (T1) => A
我的猜测是 Scala 语言设计团队更喜欢自 PartialFunction is a subclass of Function 以来更通用的返回值,宁愿让用户根据需要派生专门的代码。
示例 2:调用 calc.lift(0.5) 将抛出 MathError 而不是返回 None
从PartialFunction API doc 开始,通过andThen 组合两个偏函数将返回一个与第一个偏函数具有相同域的偏函数:
def andThen[C](k: (B) => C): PartialFunction[A, C]
因此,合成函数忽略了 inverse(0.5)(即 2.0)在第二个部分函数 arcSin 的域之外这一事实。
那么,当使用andThen 将一个函数(全部或部分)与一个部分函数组合起来时,我们如何让它返回一个具有适当域的部分函数?
与SO Q&A 中演示的类似,可以通过几个隐式类来增强andThen,以将结果组合函数的域限制为第一个函数域的子集,返回部分函数域内的值:
object ComposeFcnOps {
implicit class TotalCompose[A, B](f: Function[A, B]) {
def andThenPartial[C](that: PartialFunction[B, C]): PartialFunction[A, C] =
Function.unlift(x => Option(f(x)).flatMap(that.lift))
}
implicit class PartialCompose[A, B](pf: PartialFunction[A, B]) {
def andThenPartial[C](that: PartialFunction[B, C]): PartialFunction[A, C] =
Function.unlift(x => pf.lift(x).flatMap(that.lift))
}
}
使用示例函数进行测试:
import ComposeFcnOps._
val mod10: Int => Int = _ % 10
val inverse1: PartialFunction[Int, Double] = { case n if n != 0 => 1.0 / n }
val triple: Double => Double = _ * 3
val calc1 = mod10 andThenPartial inverse1 andThen triple
// calc1: PartialFunction[Int,Double] = <function1>
calc1.isDefinedAt(0)
// res1: Boolean = false
val inverse2: PartialFunction[Double, Double] = { case n if n != 0 => 1.0 / n }
val arcSin: PartialFunction[Double, Double] = {
case n if math.abs(n) <= 1 => math.asin(n)
}
val calc2 = inverse2 andThenPartial arcSin
// calc2: PartialFunction[Double,Double] = <function1>
calc2.isDefinedAt(0.5)
// res2: Boolean = false
calc2.lift(0.5)
// res3: Option[Double] = None