Partial & Partially Applied Functions in Scala

Partial Functions

Pattern Matching in Scala can be extended to create Partial functions, a unary function that does not support every possible value that meets the input type. Partial functions are defined only for certain domain of input values. For example, a function that returns the Square root of the input number will not work if the input number is negative.

val squareRoot: PartialFunction[Double, Double] = {

case x if x >= 0 => Math.sqrt(x)

}

The above squareRoot function is defined only for positive numbers, and returns the square root of the number. In case of negative numbers, the function returns scala.MatchError runtime exception.

A partial function can also be queried to determine if it can handle a particular value. The function isDefinedAt allows testing dynamically if a value is in the domain of the function.

squareRoot.isDefinedAt(2) shouldEqual true

squareRoot.isDefinedAt(-2) shouldEqual false

Partial functions can be chained together using orElse or andThen

val positive: PartialFunction[Int, Int] = {

case x if x >= 0 => x

}



val odd: PartialFunction[Int, Boolean] = {

case x if x % 2 == 1 => true

}



val even: PartialFunction[Int, Boolean] = {

case x if x % 2 == 0 => true

}



val evenCheck: PartialFunction[Int, Boolean] = positive andThen even



val oddCheck: PartialFunction[Int, Boolean] = positive andThen odd

Since the resulting chained function is also Partial, isDefined can be applied which checks the input against the domain of each partial function.

evenCheck.isDefinedAt(-2) shouldEqual false

evenCheck.isDefinedAt(2) shouldEqual true

This feature of Partial function becomes useful when implementing a validation system. If there are series of checks implemented to verify if the input data meets certain guidelines.

val finalCheck = check1 andThen check2 andThen check3 ...

This solution is also easily extendible as new checks are implemented they are added to the finalCheck , and same in the case of removal.

Scala allows Partial functions to be applied to collections

val greaterThan20: PartialFunction[Any, Int] = {

case i: Int if i > 20 => i

} List(1, 45, 10, "blah", true, 25) collect greaterThan20

shouldEqual List(45, 25)

The collect method takes a PartialFunction as argument and maps the values defined for this partial function over it, skipping those outside the definition domain.