In this explanation of partial functions in scala, an case statement is used as follows:
val divide2: PartialFunction[Int, Int] = {
case d: Int if d != 0 => 42 / d
}
Then it says:
Although this code doesn’t explicitly implement the isDefinedAt method, it works exactly the same as the previous divide function definition
The previous divide function explicitly defines apply and isDefined methods as follows:
val divide = new PartialFunction[Int, Int] {
def apply(x: Int) = 42 / x
def isDefinedAt(x: Int) = x != 0
}
I see that the compiler is doing some sort of magic here, but I'm curious about the nature of the mechanism: is the guard in the case (if d != 0) extracted into body of isDefined at the background? What about multiple case statements with their own guards? The domain of the function is defined in the case statement(s), so that must be how the compiler is building the implementation of isDefined behind the scenes.
Am I right?
First of all, the explanation is wrong, since both the presented examples don't work the same:
scala> val a: PartialFunction[Int, Int] = {
| case i if i != 0 => 42 / i
| }
a: PartialFunction[Int,Int] = <function1>
scala> val b = new PartialFunction[Int, Int] {
| def apply(i: Int) = 42 / i
| def isDefinedAt(i: Int) = i != 0
| }
b: PartialFunction[Int,Int] = <function1>
scala> a(0)
scala.MatchError: 0 (of class java.lang.Integer)
at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:253)
at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:251)
at $anonfun$1.applyOrElse(<console>:7)
at $anonfun$1.applyOrElse(<console>:7)
at scala.runtime.AbstractPartialFunction$mcII$sp.apply$mcII$sp(AbstractPartialFunction.scala:36)
... 33 elided
scala> b(0)
java.lang.ArithmeticException: / by zero
at $anon$1.apply$mcII$sp(<console>:8)
... 33 elided
But the core of it is true, the compiler converts the code in the case clauses to a boolean expression for the isDefinedAt
method and then the function itself as the apply method (hence the MatchError if the value is not defined)
For a general example, a function like this:
val a = PartialFunction[A, B] {
case <clause A> => <action A>
case <clause B> => <action B>
}
Is compiled as:
val a = new PartialFunction[A, B] {
def isDefinedAt(a: A) = a match {
case <clause A> => true
case <clause B> => true
case _ => false
}
def apply(a: A) = a match {
case <clause A> => <action A>
case <clause B> => <action B>
}
}
Here is the spec that defines this behavior.