Roman Parykin

Software development, distributed systems, & natural language processing

Scala Function object to the rescue

Apr 1, 2015

The Function object has been there since Scala version 1.0. It provides a bunch of utility methods for dealing with higher-order functions. Despite its simplicity and usefulness, I have found that not only do many novice developers not use them, but they are also unaware of their existence. In this post, I would like to bring to your attention some of the functions I find helpful in certain scenarios.

chain

To get started, let’s introduce two extremely simple functions from Int to Int. One of them, let’s call it inc, will increase a number by one, while the other one, double, with multiply a number by two. We could use a shorter definition form such as val inc: Int => Int = _ + 1 for both functions.

scala> val inc: Int => Int = x => x + 1
scala> val double: Int => Int = x => x * 2

So what do we do when we have a sequence of such functions that we want to combine? One of the most popular options in my practice is the following:

scala> List(inc, double, inc) reduce (_ andThen _)

This piece of code takes a sequence of functions and combines them using the andThen method starting with the first one in the list and returns the resulting function from Int to Int. It is by no means a bad code. Let’s see how we can simplify it by using the chain function from the Function object:

scala> Function.chain(List(inc, double, inc))

It does exactly the same as the previous snippet but it might be more intuitive. If we first import all the functions from the Function object, it will be dead simple:

scala> import Function._
scala> chain(List(inc, double, inc))

If you want to use the compose function instead of the andThen, you can still achieve the same with the chain by reversing the sequence first:

scala> chain(List(inc, double, double).reverse)

tupled

Imagine there is a tuple of two elements, id and name, representing a user wrapped into Option:

scala> val user: Option[(Int, String)] = Some(1, "Bob")

And a function, let’s call it auth, which accepts id and name as two separate arguments:

scala> val auth: (Int, String) => Boolean = (id, name) => id == 1 && name == "Bob"

We can’t just map the auth function over the user because the former expects two arguments while our user is a tuple with two elements. One option would be to extract the id and name and pass them as separate arguments to the auth function but it requires some boilerplate to write. That’s when we can use the tupled function from the Function object. It takes a function with, let’s say, two arguments, and converts it into a function that takes a tuple of two elements with the same types as the original arguments. That’s exactly what we need to map the auth function over the user:

scala> user map auth.tupled
res4: Option[Boolean] = Some(true)

There are tupled functions defined for functions with arity from two to five inclusive. I find it convenient as I don’t use tuples of more than two or three elements that often.

Technically, in the previous example, the tupled was called on the function itself (as it’s defined both in the Function object and the Function* traits). Another “trick” that can be useful is mapping over a hash map in what many people would say a natural way. (It also demonstrates the usage of the tupled function from the Function object and not defined in the Function* trait.) In Scala, you can’t write code like this:

scala> val m = Map(1 -> "first", 2 -> "second")
scala> m map { (k, v) => s"$k:$v" }
<console>:12: error: missing parameter type
Note: The expected type requires a one-argument function accepting a 2-Tuple.
      Consider a pattern matching anonymous function, `{ case (k, v) =>  ... }`
              m map { (k, v) => s"$k:$v" }
                       ^
<console>:12: error: missing parameter type
              m map { (k, v) => s"$k:$v" }

What many developers would do is something like this:

scala> m map { case (k, v) => s"$k:$v" }
res5: scala.collection.immutable.Iterable[String] = List(1:first, 2:second)

You can achieve the same using the tupled function:

scala> m map tupled { (k, v) => s"$k:$v" }
res6: scala.collection.immutable.Iterable[String] = List(1:first, 2:second)

Maybe not so useful but it helps to understand its application.

unlift

To illustrate the usage of the unlift, let’s write a function that takes an Int and returns Some(x) if x is equal to or greater than zero, and None otherwise:

scala> val f: Int => Option[Int] = Option(_) filter (_ >= 0)

The unlift function turns an A => Option[B] function into a PartialFunction[A, B]. It allows us to use our function in any place where a partial function is required. To make it clear, this how we can use our function to filter out negative integers from a list:

scala> import Function._
scala> List(-1,0,1) collect unlift(f)
res7: List[Int] = List(0, 1)

I don’t use this on a daily basis but there are cases like this where it comes very handy.

Bonus: there is an opposite function defined in the PartialFunction called lift. To see how it is related to the unlift function, the following equation is always true:

scala> f == unlift(f).lift
res8: Boolean = true

uncurried/untupled

These two functions are the opposite to the curried and the tupled functions, respectively. I haven’t seen them being used as frequently as the ones described above.

Although there is nothing new, I believe that we often overlook some of the useful API provided by the Scala standard library. I hope that this refresher is on time and can save you a few lines of code every now and then.