Partially-Applied Functions (and Currying)
My motivations for writing this lesson are a little different than usual. Typically I think, “You’ll want to know this feature so you can use it like ___,” but the first motivation for this lesson goes like this: You’ll want to know about the concept of “currying” because experienced FP developers talk about it a lot, especially if they have Haskell programming experience. (I did mention that Haskell was named after Haskell Curry, didn’t I?)
A second motivation is that the concept of currying is related to the multiple parameter groups I showed in the previous lesson come from.
The primary motivation for writing this lesson is that having multiple parameter groups make it a little easier to create partially-applied functions, and these can be useful in your FP code.
I’ll cover all of these topics in this lesson.
Given that introduction, the goals of this lesson are:
Provide a definition of currying
Show how to create partially-applied functions from functions that have (a) multiple parameter groups or (b) single parameter groups
I’ll also show how to create “curried” functions from regular functions, and show how Scala gets these features to work with the JVM.
When I first got started in FP, I got lost in some of the nomenclature, and “currying” was a particularly deep rabbit’s hole of “Time in My Life I Wish I Had Spent Differently.”
All that the theory of currying means is that a function that takes multiple arguments can be translated into a series of function calls that each take a single argument. In pseudocode, this means that an expression like this:
result = f(x)(y)(z)
is mathematically the same as something like this:
f1 = f(x)
f2 = f1(y)
result = f2(z)
That’s all it means. The Wikipedia page on Currying describes the theory of currying like this:
In mathematics and computer science, currying is the technique of translating the evaluation of a function that takes multiple arguments into evaluating a sequence of functions, each with a single argument.
They later state:
There are analytical techniques that can only be applied to functions with a single argument. Practical functions frequently take more arguments than this.
In my daily working life, this sort of theory usually isn’t important. It’s one of those things that’s “nice to know,” but the important things are really (a) how this impacted the design of the Scala language, and (b) what you can do because of this theory.
In Scala this seems to fit most naturally with functions that have multiple input parameters groups, and I’ll demonstrate that in this lesson.
In the remainder of this lesson I’ll occasionally use the acronym “PAF” to mean “partially-applied function.”
To understand PAFs, I’ll start with two definitions from this online JavaScript course:
Application: The process of applying a function to its arguments in order to produce a return value.
As in algebra, in FP you say that “a function is applied to its arguments,” so “Application” in this context can also be called “Full Application,” or “Complete Application.”
- Partial Application: This is the process of applying a function to some of its arguments. A partially-applied function gets returned for later use. In other words, a PAF is a function that takes a function with multiple parameters and returns a function with fewer parameters.
The best way to explain PAFs is with examples, so let’s look at a few.
The following example shows how PAFs work. In the first step, you define a function with multiple parameter groups:
scala> def plus(a: Int)(b: Int) = a + b
plus: (a: Int)(b: Int)Int
Next, rather than giving the function all of the parameters in the two parameter groups it specifies, you give it (a) the parameter for the first group (a
), and (b) a placeholder for the parameter in the second list, the ubiquitous underscore character:
scala> def plus2 = plus(2)(_)
plus2: Int => Int
The REPL output shows that this creates a new function named plus2
which has the type Int => Int
. This means that plus2
takes an Int
as input, and returns an Int
as a result.
At this point you can think of plus2
as looking like this:
def plus(b: Int) = 2 + b
plus2
has been “seeded” with the initial Int
value 2
, and now it’s just sitting there, waiting for another Int
value that it can add to it. Let’s give it another 2
:
scala> plus2(2)
res0: Int = 4
Here’s what it looks like when you give it a 3
:
scala> plus2(3)
res1: Int = 5
As this shows, plus2
gladly adds 2
to any Int
it is given.
Before I move on to another example, note that you can create plus2
in either of these ways:
def plus2 = plus(2)(_)
def plus2 = plus(2)_
I prefer the first approach, but some people prefer the second approach.
The general benefit that this approach gives you is that it’s a way to create specialized methods from more general methods. I demonstrate that in the Scala Cookbook, and I’ll share a variation of that example here.
When you’re emitting HTML from Scala code, a wrap
function that adds a prefix and a suffix to an HTML snippet can be really useful:
def wrap(prefix: String)(html: String)(suffix: String) = {
prefix + html + suffix
}
You can use that function to do something like this, where I wrap a string in opening and closing <div>
tags:
val hello = "Hello, world"
val result = wrap("<div>")(hello)("</div>")
Of course that <div>
tag can be more complicated, such as specifying a CSS class
or id
, but I’m keeping this simple.
It turns out that wrap
is a really nice, general function, so you can wrap text in DIV
tags, P
tags, SPAN
tags, etc. But if you’re going to be wrapping a lot of strings with DIV
tags, what you probably want is a more specific wrapWithDiv
function. This is a great time to use a partially-applied function, because that’s what they do, helping you create a specific function from a general function:
val wrapWithDiv = wrap("<div>")(_: String)("</div>")
Now you can call wrapWithDiv
, just passing it the HTML you want to wrap:
scala> wrapWithDiv("<p>Hello, world</p>")
res0: String = <div><p>Hello, world</p></div>
scala> wrapWithDiv("<img src=\"/images/foo.png\" />")
res1: String = <div><img src="/images/foo.png" /></div>
As a nice benefit, you can still call the original wrap
function:
wrap("<pre>", "val x = 1", "</pre>")
and you can also create other, more-specific functions:
val wrapWithPre = wrap("<pre>")(_: String)("</pre>")
It’s worth noting that you make a more specific function by “seeding” the more general function with one or more initial parameters. That is, you partially-apply parameters to the general function to make the specific function.
It’s necessary to specify the type of the missing parameter, as I did in this code:
val wrapWithDiv = wrap("<div>")(_: String)("</div>")
---------
If you don’t specify the type, you’ll get a compiler error that looks like this:
scala> val wrapWithDiv = wrap("<div>")(_)("</div>")
<console>:11: error: missing parameter type for
expanded function ((x$1) => wrap("<div>")(x$1)("</div>"))
val wrapWithDiv = wrap("<div>")(_)("</div>")
^
As a summary, PAFs give you this capability:
You write a general function
You create a specific function from the general function
You still have access to both functions, and you kept your code “DRY” — you didn’t copy and paste code to make the new function
As a fun example of some things you can do with PAFs, the “partially-applied functions” section of the Scala Exercises website demonstrates that you can create curried functions from “normal” Scala functions. For instance, you can start with a “normal” one-parameter group function like this:
def add(x: Int, y: Int) = x + y
Then they show that you can create a Function2
instance from add
by adding an underscore after it, like this:
scala> val addFunction = add _
addFunction: (Int, Int) => Int = <function2>
They then prove that it’s a Function2
instance like this:
(add _).isInstanceOf[Function2[_, _, _]]
This technique of converting a def
method into a true function uses a Scala technology known as “Eta Expansion.” I mentioned this in the previous lessons, and I also discuss it in depth in the appendix titled, “The Differences Between ‘def’ and ‘val’ When Defining Functions.”
Then they create a “curried” function from that Function2
instance:
val addCurried = (add _).curried
Now you can use the new curried function like this:
addCurried(1)(2)
As this shows, calling the curried
method on the add
function instance creates a new function that has two parameter groups. (So, a curried function can be thought of as a function with multiple parameter groups.)
It’s also easy to create a partially-applied function from the curried function, like this:
val addCurriedTwo = addCurried(2) // create a PAF
addCurriedTwo(10) // use the PAF
You can see how all of those steps work by pasting the code into the REPL:
scala> def add(x: Int, y: Int) = x + y
add: (x: Int, y: Int)Int
scala> (add _).isInstanceOf[Function2[_, _, _]]
res0: Boolean = true
scala> val addCurried = (add _).curried
addCurried: Int => (Int => Int) = <function1>
scala> addCurried(1)(2)
res1: Int = 3
scala> val addCurriedTwo = addCurried(2)
addCurriedTwo: Int => Int = <function1>
scala> addCurriedTwo(10)
res2: Int = 12
Personally, I mostly use curried functions to create control structures — as I demonstrated with whilst
and ifBothTrue
in the previous lesson. So, at the moment, this is a technique I know about, but have not used.
So far I’ve shown that you can create a partially-applied function with functions that have multiple parameter groups, but because Scala is really convenient, you can create PAFs with single parameter group functions as well.
To do this, first define a function as usual, with one parameter group:
def wrap(prefix: String, html: String, suffix: String) = {
prefix + html + suffix
}
Then create a PAF by applying the first and third parameters, but not the second:
val wrapWithDiv = wrap("<div>", _: String, "</div>")
The wrapWithDiv
function you create in this manner works the same as the wrapWithDiv
function created in the previous example:
scala> val wrapWithDiv = wrap("<div>", _: String, "</div>")
wrapWithDiv: String => String = <function1>
scala> wrapWithDiv("Hello, world")
res1: String = <div>Hello, world</div>
If you’re interested in how things work under the covers, a good question at this point is, “How can this stuff possibly work with the JVM?” The JVM certainly wasn’t written to account for things like currying and PAFs, so how does any of this work?
A short answer is that (a) the Scala compiler “uncurries” your code, and (b) you can see this during the compilation process. For example, write a little Scala class like this:
class Currying {
def f1(a: Int, b: Int) = { a + b } // 1 param group
def f2(a: Int)(b: Int) = { a + b } // 2 param groups
}
Then compile that class with this command:
$ scalac -Xprint:all Currying.scala
if you dig through the end of that output, you’ll see that the Scala compiler has an “uncurry” phase. A short version of the tail end of the compiler output looks like this:
[[syntax trees at end of typer]] // Currying.scala
package <empty> {
class Currying extends scala.AnyRef {
def <init>(): Currying = {
Currying.super.<init>();
()
};
def f1(a: Int, b: Int): Int = a.+(b);
def f2(a: Int)(b: Int): Int = a.+(b)
}
}
.
.
.
[[syntax trees at end of uncurry]] // Currying.scala
package <empty> {
class Currying extends Object {
def <init>(): Currying = {
Currying.super.<init>();
()
};
def f1(a: Int, b: Int): Int = a.+(b);
def f2(a: Int, b: Int): Int = a.+(b)
}
}
As that output shows, I wrote the two functions f1
and f2
differently, but after the compiler’s “uncurry” phase they end up looking the same.
Things might look more interesting in the output if I had created a partially-applied function, but I’ll leave that as an exercise for the reader.
If you want to dig into this more, it can also help to know what the Scala compiler phases are. This command:
$ scalac -Xshow-phases
shows that the phases in Scala 2.11 are:
phase name id description
---------- -- -----------
parser 1 parse source into ASTs, perform simple desugaring
namer 2 resolve names, attach symbols to named trees
packageobjects 3 load package objects
typer 4 the meat and potatoes: type the trees
patmat 5 translate match expressions
superaccessors 6 add super accessors in traits and nested classes
extmethods 7 add extension methods for inline classes
pickler 8 serialize symbol tables
refchecks 9 reference/override checking, translate nested objects
uncurry 10 uncurry, translate function values to anonymous classes
tailcalls 11 replace tail calls by jumps
specialize 12 @specialized-driven class and method specialization
explicitouter 13 this refs to outer pointers
erasure 14 erase types, add interfaces for traits
posterasure 15 clean up erased inline classes
lazyvals 16 allocate bitmaps, translate lazy vals into lazified defs
lambdalift 17 move nested functions to top level
constructors 18 move field definitions into constructors
flatten 19 eliminate inner classes
mixin 20 mixin composition
cleanup 21 platform-specific cleanups, generate reflective calls
delambdafy 22 remove lambdas
icode 23 generate portable intermediate code
jvm 24 generate JVM bytecode
terminal 25 the last phase during a compilation run
As that shows, the “uncurry” phase “translates function values to anonymous classes.”
The concepts of currying and partially-applied functions are closely related, but they aren’t exactly the same. As I wrote at the beginning, currying is defined like this:
A function that takes multiple arguments can be translated into a series of function calls that each take a single argument.
This is particularly important in a language like Haskell, where all functions are technically curried functions. In Scala this is generally a theoretical thing that’s good to know about, and it’s good to know that you can create a curried function from an uncurried function, but these aren’t “core” features you absolutely need to know to write code in Scala.
A partially-applied function on the other hand is a function that you manually create by supplying fewer parameters than the initial function defines. As I showed in this lesson, you can create the PAF plus2
like this:
def plus(a: Int)(b: Int) = a + b
def plus2 = plus(2)(_)
and you can create wrapWithDiv
as a PAF like this:
val wrapWithDiv = wrap("<div>")(_: String)("</div>")
If for some reason you want to partially-apply one parameter out of three, you can also do this:
def add(a: Int)(b: Int)(c: Int) = a + b + c
val add2NumbersTo10 = add(10)(_: Int)(_: Int)
So both concepts are related to multiple parameter groups, but in general, I use PAFs more often than I concern myself with curried functions.
As I mentioned at the beginning of this lesson, don’t get bogged down in the precise meaning of things like “curried functions.” It is good to know how multiple input parameter groups work because it’s a technique that is used a lot in Scala/FP, but don’t get lost in worrying about the exact meaning of currying like I did. Understanding how multiple parameter groups work is the important thing.
This lesson covered the following topics:
It provides a definition of currying
It shows how to create partially-applied functions from functions that have (a) multiple parameter groups or (b) single parameter groups
It also shows how to create “curried” functions from regular functions, and provided a little look at how Scala gets these features to work with the JVM.
I’ve covered a lot of Scala/FP background material so far, but occasionally I had to mix in a few var
fields in my examples because that’s the only way to solve certain problems with the tools I’ve shown so far.
Well, no more of that.
In the next few lessons things are going to be fun, as I get to cover recursion. Once you understand recursive calls, I think you’ll find that they’re a natural way to think about writing iterative algorithms.
Once I cover recursion you’ll then be very close to handling many more FP concepts, the first of which will be how to handle “state” in FP applications. But to handle state in an FP manner, you’ll need to know how to write recursive functions …
Here are a few more resources related to currying and partially-applied functions.
- Daniel Westheide’s article, Currying and Partially Applied Functions is a good resource.
These discussions on StackOverflow and StackExchange also provide a little more insight: