Skip to content

Scala Partial Function!

At this moment I do not have a personal relationship with a computer.

Janet Reno

In Scala, partial functions are functions that are only defined for a subset of possible input values. They are typically defined using the PartialFunction trait,

(Note: Do not confuse with Partially Applied Function)

The signature of the PartialFunction trait looks like this:

trait PartialFunction[-A, +B] extends (A) => B

Note: The => symbol can be thought of as a transformer, and in this case, the (A) => B can be interpreted as a function that transforms a type A into a resulting type B.

Note: the symbols + and - can be used as part of a parameter declaration to indicate whether the parameter is covariant (+) or contravariant (-) with respect to its type parameter. Refer covariant and contravariant parameters.

To define a partial function we have various approaches:

Approach 1 : Implement two methods namely isDefinedAt and apply:

val convertLowNumToString = new PartialFunction[Int, String] {
    val nums = Array("one", "two", "three", "four", "five")
    def apply(i: Int) = nums(i-1)
    def isDefinedAt(i: Int) = i > 0 && i < 6
}

OR

val convertLowNumToString => PartialFunction[Int, String] = (numInString: String) => {
    new PartialFunction[Int, String] {
    val nums = Array("one", "two", "three", "four", "five")
    def apply(i: Int) = nums(i-1)
    def isDefinedAt(i: Int) = i > 0 && i < 6
    }
}

Approach 2 : Another way to declare partial function is to use the case statement from the scala match case statement.:

val pf1: PartialFunction[Int, String] = {
case 1 => "one"
case 2 => "two"
}

val pf2: PartialFunction[Int, String] = {
case 3 => "three"
case 4 => "four"
}

Chaining Partial Functions:

Partial Function has two important methods: orElse and andThen.

For example if we want a method to be transformed an input Int into an output returned a String instead, it would be declared like this below:

PartialFunction[Int, String]

orElse is a method that takes another partial function as an argument, and returns a new partial function that is the result of chaining the two functions together. The resulting function is defined for any input value that is defined by either of the two input functions.

Here’s an example of orElse in action:

// Method declaration
override def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1])

pf = pf1.orElse(pf2)

println(pf(1)) // Output: one
println(pf(3)) // Output: three
println(pf.isDefinedAt(5)) // Output: false

In this example, pf1 and pf2 are two partial functions that are defined for certain input values of type Int. We can create a new partial function pf by calling the orElse method on pf1 with pf2 as the argument. The resulting function is defined for any input value that is defined by either pf1 or pf2, but not for any other values.

andThen is a method that takes another function as an argument, and returns a new partial function that is the result of applying the input function to the output of the original function. The resulting function is defined for any input value that is defined by the original function.

Here’s an example of andThen in action:

// Method declaration
override def andThen[C](k: B => C): PartialFunction[A, C]

val pf: PartialFunction[Int, String] = {
  case 1 => "one"
  case 2 => "two"
}

val f: String => String = s => s.toUpperCase

val pf2 = pf.andThen(f)

println(pf2(1)) // Output: ONE
println(pf2(2)) // Output: TWO
println(pf2.isDefinedAt(3)) // Output: false

In this example, pf is a partial function that is defined for certain input values of type Int. We can create a new function f that takes a string and returns its uppercase version. We can then create a new partial function pf2 by calling the andThen method on pf with f as the argument. The resulting function applies f to the output of pf for any input value that is defined by pf.

Published inPersonal PostsScalaTechnical Posts