Monthly Archives: December 2019

Composing Partial Functions In Scala

Just like partial functions in mathematics, a partial function in Scala is a function whose domain doesn’t cover all elements of the domain’s data type. For example:

It’s a function defined for all non-zero integers, but f(0) would produce a java.lang.ArithmeticException.

By defining it as a partial function like below:

we will be able to leverage PartialFunction’s methods like isDefinedAt to check on a given element before applying the function to it.

Methods lift and unlift

Scala provides a method lift for “lifting” a partial function into a total function that returns an Option type. Using the above partial function as an example:

Simple enough. Conversely, an Option-typed total function can be “unlifted” to a partial function. Applying unlift to the above function f would create a new partial function same as pf:

Function compositions

For simplicity, we’ll look at only functions with arity 1 (i.e. Function1, which takes a single argument). It’s trivial to use the same concept to apply to FunctionN.

Methods like andThen and compose enable compositions of Scala functions. Since both methods are quite similar, I’m going to talk about andThen only. Readers who would like to extend to compose may try it as a programming exercise.

Method andThen for Function1[T1, R] has the following signature:

A trivial example:

Now, let’s replace the 2nd function add1 with a partial function inverse:

Note that doubleThenInverse still returns a total function even though the composing function is partial. That’s because PartialFunction is a subclass of Function:

hence method andThen rightfully returns a total function as advertised.

Unfortunately, that’s undesirable as the resulting function lost the inverse partial function’s domain information.

Partial function compositions

Method andThen for PartialFunction[A, C] has its signature as follows:

Example:

That works perfectly, since any given element not in the domain of any of the partial functions being composed should have its corresponding element(s) eliminated from the domain of the composed function. In this case, 0.5 is not in the domain of pfMap, hence its corresponding element, 2 (which would have been inverse-ed to 0.5), should not be in inverseThenPfMap‘s domain.

Unfortunately, I neglected to mention I’m on Scala 2.13.x. For Scala 2.12 or below, inverseThenPfMap.isDefinedAt(2) would be true.

Turning composed functions into a proper partial function

Summarizing what we’ve looked at, there are two issues at hand:

  1. If the first function among the functions being composed is a total function, the composed function is a total function, discarding domain information of any subsequent partial functions being composed.
  2. Unless you’re on Scala 2.13+, with the first function being a partial function, the resulting composed function is a partial function, but its domain would not embody domain information of any subsequent partial functions being composed.

To tackle the issues, one approach is to leverage implicit conversion by defining a couple of implicit methods that handle composing a partial function on a total function and on a partial function, respectively.

Note that the implicit methods are defined as methods within implicit class wrappers, a common practice for the implicit conversion to carry out by invoking the methods like calling class methods.

In the first implicit class, function f (i.e. the total function to be implicitly converted) is first transformed to return an Option, chained using flatMap to the lifted partial function (i.e. the partial function to be composed), followed by an unlift to return a partial function.

Similarly, in the second implicit class, function pf (i.e. the partial function to be implicitly converted) is first lifted, chained to the lifted partial function (i.e. the partial function to be composed), followed by an unlift.

In both cases, andThenPF returns a partial function that incorporates the partial domains of the functions involved in the function composition.

Let’s reuse the double and inverse functions from a previous example:

Recall from that example that doubleThenInverse is a total function. Now, let’s replace andThen with our custom andThenPF:

The resulting function is now a partial function with the composing function’s partial domain as its own domain. I’ll leave testing for the cases in which the function to be composed is a partial function to the readers.