Very good question.
Let's check the source and see what's happening under the covers:
override def andThen[C](k: B => C): PartialFunction[A, C] =
new AndThen[A, B, C] (this, k)
We can observe here that andThen
doesn't even expect a Partial Function, any Function that transforms the result will do. Your code works, because: trait PartialFunction[-A, +B] extends (A => B)
. This can actually be found in the documentation:
def andThen[C](k: (B) ⇒ C): PartialFunction[A, C]
Composes this partial function with a transformation function that gets applied to results of this partial function.
C the result type of the transformation function.
k the transformation function
returns a partial function with the same domain as this partial function, which maps arguments
x
tok(this(x))
.
So there's currently no way to chain PartialFunction
s in the way you would like, because as Robin said, it would require applying the function. Next to being computationally expensive it could also have side effects, which is a bigger problem.
Update
Whipped together an implementation you're looking for. Use it cautiously! As I already mentioned, if your code has side effects it will cause problems:
implicit class PartialFunctionExtension[-A, B](pf: PartialFunction[A, B]) {
def andThenPf[C](pf2: PartialFunction[B, C]) = new PfAndThen(pf, pf2)
class PfAndThen[+C](pf: PartialFunction[A, B], nextPf: PartialFunction[B, C]) extends PartialFunction[A, C] {
def isDefinedAt(x: A) = pf.isDefinedAt(x) && nextPf.isDefinedAt(pf.apply(x))
def apply(x: A): C = nextPf(pf(x))
}
}
Trying it out:
deepTestLvl1.andThenPf(deepTestLvl2).isDefinedAt(Some(Some(3))) // false
deepTestLvl1.andThenPf(deepTestLvl2).isDefinedAt(Some(None)) // true
deepTestLvl1.andThenPf(deepTestLvl2).apply(Some(None)) // 3