Scala - Partial Functions



You can define functions that are not necessarily defined for all possible input values. Partial functions can be used to handle cases where a function can only be applied to a subset of possible inputs.

Partial Functions

Partial function is an instance of the PartialFunction trait. This trait defines methods for checking whether a function is defined at a given input. It is also used for applying the function to that input.

Understanding Partial Functions

Let us take an example of a partial function that handles division, but only for non-zero divisors. The isDefinedAt method checks if the divisor is non-zero. The apply method performs the division.

The isDefinedAt method is what makes a function partial. It checks if a given input satisfies the conditions for which the function is defined. If the condition is met, the function can be applied to the input.

The apply method is used to apply the function to the input if the conditions are met. If the conditions are not met and the function is applied, it will throw a MatchError.

In this example, we will define a partial function called divide that divides two integers only if the divisor is not zero.

Syntax

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

In this example, the partial function divide is defined for all pairs (num, den) where den is not zero. If the divisor den is zero, the function is not defined.

Example

Following example program shows a partial function for division -

object Demo {
   def main(args: Array[String]) = {
      val divide: PartialFunction[(Int, Int), Int] = {
         case (num, den) if den != 0 => num / den
      }

      val pairs = List((6, 3), (4, 0), (9, 3))

      pairs.foreach { pair =>
         if (divide.isDefinedAt(pair)) {
            println(s"Result of ${pair._1} / ${pair._2} is ${divide(pair)}")
         } else {
            println(s"Cannot divide ${pair._1} by ${pair._2}")
         }
      }
   }
}

Save the above program in Demo.scala. Use the following commands to compile and execute this program.

Command

> scalac Demo.scala
> scala Demo

Output

Result of 6 / 3 is 2
Cannot divide 4 by 0
Result of 9 / 3 is 3

Chaining Partial Functions

You can chain partial functions together using the orElse method. So you can create a composite function that handles multiple cases.

Example

Consider a partial function that handles different types of input values and chains them together.

Let us take an example of chaining partial functions to handle multiple cases. We will define three partial functions: intHandler, stringHandler, and doubleHandler. Each function handles a different type of input.

The intHandler handles integers, the stringHandler handles strings, and the doubleHandler handles doubles. We will then chain these partial functions using the orElse method to create a composite function called handler.

Syntax

val intHandler: PartialFunction[Any, String] = {
   case i: Int => s"Int: $i"
}

val stringHandler: PartialFunction[Any, String] = {
   case s: String => s"String: $s"
}

val doubleHandler: PartialFunction[Any, String] = {
   case d: Double => s"Double: $d"
}

val handler = intHandler orElse stringHandler orElse doubleHandler

Example

Following example program shows chaining partial functions to handle multiple cases -

object Demo {
   def main(args: Array[String]) = {
      val intHandler: PartialFunction[Any, String] = {
         case i: Int => s"Int: $i"
      }

      val stringHandler: PartialFunction[Any, String] = {
         case s: String => s"String: $s"
      }

      val doubleHandler: PartialFunction[Any, String] = {
         case d: Double => s"Double: $d"
      }

      val handler = intHandler orElse stringHandler orElse doubleHandler

      val inputs = List(42, "Scala", 3.14, true)

      inputs.foreach { input =>
         if (handler.isDefinedAt(input)) {
            println(handler(input))
         } else {
            println(s"Unhandled input: $input")
         }
      }
   }
}

Save the above program in Demo.scala. Use the following commands to compile and execute this program.

Command

> scalac Demo.scala
> scala Demo

Output

Int: 42
String: Scala
Double: 3.14
Unhandled input: true

In this example, the partial functions intHandler, stringHandler, and doubleHandler are chained together to handle different types of input values. The composite function handler is then used to process a list of inputs. So it can handle for integers, strings, and doubles, and default case of other types.

Combining Partial Functions with collect

The collect method is for applying partial functions to collections. You can filter and transform elements in a collection using a partial function.

Example

There is list of mixed values. You also have partial function that extracts integers and doubles from the list.

Let us take example of using the collect method to apply partial function to collection. We will define this partial function that called extractNumbers. It extracts integers and doubles from a list of mixed values.

The extractNumbers partial function converts integers to doubles. It keeps doubles as these are. The collect method will then apply the partial function to the list. So there will be a new list containing only the extracted numbers.

Syntax

val extractNumbers: PartialFunction[Any, Double] = {
   case i: Int => i.toDouble
   case d: Double => d
}

Following example program shows how to use the collect method with a partial function to extract numbers from a list -

Example

object Demo {
   def main(args: Array[String]) = {
      val values = List(1, "Scala", 2.5, 3, "Java", 4.0)

      val extractNumbers: PartialFunction[Any, Double] = {
         case i: Int => i.toDouble
         case d: Double => d
      }

      val numbers = values.collect(extractNumbers)

      println(s"Extracted numbers: $numbers")
   }
}

Save the above program in Demo.scala. Use the following commands to compile and execute this program.

Command

> scalac Demo.scala
> scala Demo

Output

Extracted numbers: List(1.0, 2.5, 3.0, 4.0)

In this example, the extractNumbers partial function extracts integers and doubles from the values list. It converts integers to doubles. The collect method applies the partial function to the list. So, there will be a new list containing only the extracted numbers.

Partial Functions Summary

  • You can define partial functions using the PartialFunction It includes the isDefinedAt and apply methods.
  • The isDefinedAt method checks if a function is defined for a given input. Whereas the apply method applies the function to the input.
  • You can define partial functions using the case syntax to specify the inputs for which these are defined.
  • You can chain partial functions using the orElse method to create composite functions that handle multiple cases.
  • You can filter and transform collections using partial functions.
  • You can handle various vases in a concise and expressive manner using partial functions.
Advertisements