Know About Partial Functions in Scala under 3 Minutes

Reading Time: 3 minutes

Functions and methods are essential components of programming. They help us to process input and provide us with output.

In this blog, we will learn about partial functions through examples in Scala.

Note: Do not confuse it with partially defined functions.

What are partial functions?

In English, “partial” means something which is not complete. Similarly, a partial function is a function applicable only for a subset of the input values over which we define it.

Let’s understand this through an example where we create a function to perform Integer division.

val divide: (Int, Int) => Int = (num: Int, den: Int) => num / den

The above divide function is a partial function as it is not defined for all the possible int values of the denominator i.e. 0 in this case.

PartialFunction trait in Scala

PartialFunction is a trait present in the Scala standard library. As per Scala docs:

A partial function of type PartialFunction[A, B] is a unary function where the domain does not necessarily include all values of type A. The function isDefinedAt allows to test dynamically if a value is in the domain of the function.

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

Let’s break down the definition part by part.

Firstly, it says a PartialFunction is a unary function i.e. it takes only one argument and of type A.

Secondly, we do not define the function is for all the possible values of Type A.

Lastly, we can check whether the function is defined for a particular value or not using the isDefinedAt function.

Writing Partial Functions in Scala

There are 2 ways in which we can rewrite our divide function using PartialFunction:

val betterDivide: Int => PartialFunction[Int, Int] = (num: Int) => {
    new PartialFunction[Int, Int] {
      override def isDefinedAt(den: Int): Boolean = den != 0

      override def apply(den: Int): Int = num / den
    }
}

There is another way in which we can define the above division function.

The second version is less verbose than the first one.

val betterDivide2: Int => PartialFunction[Int, Int] = (num: Int) => {
    case den: Int if den != 0 => num / den
}

Utilizing the above partial functions

This is how we invoke the above 2 variants of the function:

 // Invoking First Version
  val divide4By: PartialFunction[Int, Int] = betterDivide(4) // 4: Numerator
  if(divide4By.isDefinedAt(2)) println(divide4By(2)) // 2: Denominator
  else println("Invalid Division")
// OUTPUT: 2

  if(divide4By.isDefinedAt(0)) println(divide4By(0)) // 0: Denominator
  else println("Invalid Division")
// OUTPUT: Invalid Division


// Invoking Second Version
  val divide6By = betterDivide2(6)
  if(divide6By.isDefinedAt(2)) println(divide6By(2))
  else println("Invalid Division")
// OUTPUT: 3

  if(divide6By.isDefinedAt(0)) println(divide6By(0))
  else println("Invalid Division")
// OUTPUT: Invalid Division

Chaining Partial Functions

The Scala standard library also provides some useful functions in the PartialFunction trait:

1. orElse

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

// Usage
partialFunction1 orElse partialFunction2

If partialFunction1’s isDefined method returns false then partialFunction2 is executed

2. andThen:

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

// Usage
val divide6AndIncrement = betterDivide2(6) andThen(_ + 1)
if(divide6AndIncrement.isDefinedAt(2)) println(divide6AndIncrement(2))
else println("Invalid Division")

// OUTPUT: 4

andThen method takes a function and applies it to the result of the partial function.

Conclusion

After going through this blog the readers hopefully understand what are partial functions. Many Scala libraries like the collection library and other APIs like Akka make use of Partial functions.

For example, the Receive type in Akka Actors is nothing but a partial function from Any => Unit.

So I hope the next time we come across Partial Functions we will be less intimidated by it and would be able to make better sense of the code.

knoldus