Learn through the super-clean Baeldung Pro experience:
>> Membership and Baeldung Pro.
No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.
Last updated: March 26, 2025
In this tutorial, we will be looking at partial functions in Scala. A partial function is a function applicable to a subset of the data it has been defined for.
For example, we could define a function on the Int domain that only works on odd numbers.
If we look at the Scaladoc definition, there are three main characteristics to highlight.
A partial function:
Let’s have a look at an example of a partial function:
val squareRoot: PartialFunction[Double, Double] = {
def apply(x: Double) = Math.sqrt(x)
def isDefinedAt(x: Double) = x >= 0
}
Analyzing the above function, we can see that it:
Therefore, it is indeed a partial function.
isDefinedAt and apply are often implicitly defined. We can rewrite our function using a case statement. It’s a really common practice when writing partial functions:
val squareRootImplicit: PartialFunction[Double, Double] = {
case x if x >= 0 => Math.sqrt(x)
}
Invoking our partial function on a negative number will return a scala.MatchError runtime error.
Chaining is a very useful feature of partial functions.
Let’s say we want to convert positive integers to negative and vice-versa. Clearly, just multiplying by the integer -1 would do the trick.
However, to demonstrate the chaining feature, our solution will include:
Therefore, we can define a partial function for each requirement:
val negativeOrZeroToPositive: PartialFunction[Int, Int] = {
case x if x <= 0 => Math.abs(x)
}
val positiveToNegative: PartialFunction[Int, Int] = {
case x if x > 0 => -1 * x
}
Then we can leverage the chaining operator orElse to use them together:
val swapSign: PartialFunction[Int, Int] = {
positiveToNegative orElse negativeOrZeroToPositive
}
Additionally, the PartialFunction trait also includes andThen. As a result, chaining is really easy to achieve.
To show how andThen works, let’s begin with a function that only prints positive Int values:
val printIfPositive: PartialFunction[Int, Unit] = {
case x if x > 0 => println(s"$x is positive!")
}
We can now chain this with our swapSign function:
(swapSign andThen printIfPositive)(-1)
The final result is easy-to-chain code that reads quite nicely.
When working with collections, there are several methods that are well-suited for partial functions.
Let’s have a look at some of the main ones, behaving in a not-so-obvious way.
collect is a method that, given a collection, will return a new collection by applying a partial function to the elements in its domain.
Let’s define a partial function to be used by the collect method:
val parseRange: PartialFunction[Any, Int] = {
case x: Int if x > 10 => x + 1
}
List(15, 3, "aString") collect { parseRange }
Our partial function will only be applied to 15 since it is the only Int greater than 10 in our List.
Therefore, the above will return List(16).
We can use parseRange with map as well, without creating any compilation error:
List(15, 3, "aString") map { parseRange }
This code will throw a scala.MatchError at runtime because the pattern match doesn’t know how to handle a String.
The last method we’ll look at is filter, which is similar to collect, except filter returns a collection of elements that satisfy a provided condition.
We can see the different results returned using the same partial function to show that the collect and filter methods behave differently:
List(1, 2) collect { case i: Int => i > 10 }
List(1, 2) filter { case i: Int => i > 10 }
In this case, collect returns List(false, false), while filter returns an empty list. Also, notice how we defined the partial function as an anonymous function.
In this article, we looked at partial functions. We showed that chaining partial functions and using them with collection methods is pretty straightforward.
Whenever we’re aiming for highly composable code, partial functions are definitely something to keep in mind. When used with collections in particular, methods like collect, map, and filter make data manipulation and transformation easy.
The combination of these characteristics makes partial functions a powerful tool.