One language feature I’m a big fan of is the ability to compose functions.
A great step on the functional programming journey comes when you realise that most of the problems we are solving are just a series of functions applied one after the other. If we are just chaining functions together, do we really need to bother passing the variables between them? This is the essence of composition - combining functions together without involving variables.
I’ll be focussing on Scala, but it’s worth having a quick look at how other languages handle composition - I think Scala could learn a thing or two.
What Is It?
When we compose two functions, we create a new function which applies one function to the result of the other. If I have one function
a -> b which takes an
a and gives a
b, and another
b -> c which takes a
b and gives a
c, I should be able to combine them into one function
a -> c which takes an
a and gives a
c. No middle man.
Here’s how we do it in F#, with an example which doubles, then squares, then adds one to a number:
This is very similar to how we could write it in haskell:
In F#, the
<< is our composition function. In haskell (the undisputed king of composition), function composition is such an integral part of using the language, it is given the prized
. operator. Both languages take the mathematical approach of applying the right function first (as in
left(right(x))), with other functions available for forward composition (
>> in F#).
Scala gives us two different functions for composing backwards and forwards, called
andThen. While these names make perfect logical sense, I find them both annoyingly long and wish we had a shorter option.
If you’re like me, you will have jumped straight into the Scala REPL after learning about this, and stumbled.
Why Doesn’t This Work?
The full compiler error we get here is actually very helpful:
Unapplied methods are only converted to functions when a function type is expected. You can make this conversion explicit by writing
If we try again, following this advice, we get:
It works, just not quite as well as I had hoped. What’s with the ugly underscores and brackets? And what does the compiler mean by “unapplied methods are only converted to functions when a function type is expected”?
It turns out we’ve failed to make a distinction important to the scala compiler: methods and functions are not the same. I don’t have a good enough understanding to dive into this here, so if you want to learn more, have look at Jim McBeath’s blog on the subject.
To summarise, the methods we defined earlier, using
def, are linked with the class in which they are defined. Functions are different. They are completely self-contained, and we define them like this:
We can make more sense of the earlier error message now. We had tried to pass our methods without any arguments applied (hence “unapplied methods”) as functions, but the compiler did not know to expect a function type, so it would not compile.
This suggests that if we explicitly state that we require a function type, we should be able to pass our methods as functions:
We can also manually translate a method into a function using this syntax:
This is what was happening in the
allThree example above - the underscores after the method names are indicating that they should be passed as functions.
But surely, the
compose method takes a function as a parameter, and so the compiler should know to perform the conversion?
Not quite… unlike haskell and F#, where the compose function is defined standalone, taking two functions as parameters, Scala’s compose is defined on the
When we define a function, we are creating an instance which implements
Function1 under the hood.
This means we need to start with a function, and use the function’s compose method to join it with others. The compiler should be able to convert methods passed to
compose into functions:
A Compose Example
I’ve gone way off track, whoops. I find composition to be particularly useful when mapping over things - here’s a contrived blog example, probably not useful in real life, but hopefully gets the point across.
Let’s say we have a list of integers to which we want to apply various combinations of our functions from earlier. First of all, we need to convert all our methods to functions - I’ll explicitly declare them first this time.
I think this is pretty cool, I just really wish there was a shorter alternative to writing
andThen each time. Especially in the world of aggressive scalafmt configurations, where every character is precious.
A Shorter Alternative
Maybe we can define our own compose function to work like the haskell/F# version?
compose is only nice to use when we can make use of the infix notation, and infix is only available for methods with a single parameter - presumably this is exactly why the existing compose function is defined on the
Function1 trait. We could define our own trait with a
<< member to wrap the first function (f), but we would just end up re-writing
Function1. For the record, I ended up with this:
Don’t give up! There actually is a quite elegant solution*.
The excellent cats library provides a type class called Arrow for working with composable types, however, just like EitherT (the subject of my previous blog post), I think
Arrow suffers from some fairly incomprehensible documentation.
In the earlier example, I explicitly converted each method to a function in order compose them. Instead of doing this, we can convert them to
The first import is the Arrow type class, then we need an implicit instance of Arrow for
Function1. This, along with a bunch of other implicits, is grabbed by
import cats.implicits._ - I couldn’t work out how to only import the instance for Function1, so if you know, please get in touch. Importing absolutely everything in one statement makes me feel uneasy…
Anyway, we use
Arrow.lift to wrap our methods in arrows, and by specifying
Function1 as a type parameter we have told the compiler to treat our methods as functions. Arrows give us the
<<< functions for forward and backward composition:
As one final bonus: unlike with
>>> are functions which take two arrows as parameters, and, as long as we have an implicit instance of Arrow for Function1 in scope, the compiler can automatically convert our functions to arrows. We don’t actually need to explicitly call
Arrow.lift (just remember to import the implicits).
We are even able to pass our methods (
addOne defined way back at the top), as long as the first term the compiler hits is a function!
That’s all from me - I’ve barely scratched the surface of what you can do with Arrows (have a look at the documentation for proof), I’ve glossed over some Scala quirks with functions and methods, and type classes in Scala still behave annoyingly like magic to me. But I hope you found this useful anyway. Thanks for reading.
Edit (Thursday 27th December)
*Thanks to github user raquo for pointing out that I could use an implicit value class to implement a custom infix compose function without Arrow:
The implicit class
Compose wraps a
Function1 and extends it with two new methods. As long as
Compose is in scope, we will be able to call the new methods on any
extends AnyVal part makes this a value class, which optimises the way the code is used at runtime - I won’t go into them here but they are super useful, if you don’t know what they are, it’s worth reading up!
I also forgot to mention that self contained code examples for everything I have written about can be found here along with the source code for this blog.