A Gentle Introduction to Currying and Partial Application: y = f(a, b)(x)
Two long-established features of Functional Programming languages are currying and partial application. They offer a convenient syntax for using functions as ‘first-class values’ instead of objects to write pure, reusable and testable code.
Sometimes, they can seem obscure or idiomatic when encountered by programmers accustomed to imperative and object-oriented languages. As a result, some style guides advise against this syntax, yet other languages use it as the primary or exclusive syntax for parameter lists. This article aims to show how parameter lists can be of benefit to professional code.
What do currying and partial application look like?
Without currying, functions can only be invoked with a single list of multiple arguments (full application):
func(a, b, c)
In contrast, a fully curried function has multiple lists of single arguments:
func(a)(b)(c)
Such functions can be called with their left-most arguments only (partial application). Syntactically, the result is an intermediate function that can receive the remaining argument(s). Evaluation of the original function expression is delayed until all arguments have been supplied.
The HaskellWiki phrases it as such:
Currying is the process of transforming a function that takes multiple arguments into a function that takes just a single argument and returns another function if any arguments are still needed.
An example: function graphing
Consider a programme for graphing equations on a 2-d chart, with a standard horizontal x axis and a vertical y axis. For each x coordinate, a colourful y pixel will be plotted for each equation. In other words:
y = f(x)
Without currying, a linear equation with two constants a and b is:
y(a, b, x) = a * x + b
The only way to use this function is by invoking it with all three arguments in a single, comma-separated list. This is unfortunate for the plotter, because it’s only capable of supplying a single argument x.
With “currying”, the function can be coded as follows:
y(a, b)(x) = a * x + b
Or more descriptively:
linear(slope, intercept)(x) = slope * x + intercept
We can now create a variety of graphable functions via “partial application” of the curried function’s arguments:
horizontal = linear(0, 10)
diagonal = linear(1, 0)
triangle = linear(-1, sqrt(2))
In each case the two initial arguments have been provided, but the final x has not. At this stage, no ‘computations’ have taken place (the equation slope * x + intercept has not been evaluated). Instead, partial application has given us new functions like horizontal and diagonal, by re-using linear.
Each of these functions is a new value which, when finally invoked with the coordinate x, will map the parameters slope, intercept and x to return y:
> horizontal(1)
10
> diagonal(1)
1
> triangle(1)
.41421356237309504880
This is a perfect fit for the plotter!

To spell out an example in Scala:
> def linear(a: Double, b: Double)(x: Double): Double = a * x + b
> val diagonal = linear(1, 0) _
> diagonal(1)
1
> diagonal(2)
2
We can use this principle again and again for further re-usability:
> def horizontal(y: Double) = linear(0, y) _
> val axis = horizontal(0) // instead of axis = linear(0, 0)
> axis(0)
0
> axis(10)
0
Other languages may be more concise or more verbose.
Conciseness, DRY, single responsibility and testing
We’ve just seen how the functional style of currying and partial application has helped embody the principles of “don’t repeat yourself” and “single responsibility”. It has also “separated the concerns” of configuring the function versus running it. Compared to object-oriented (subclassing) syntax, this has been achieved concisely without the need for excessive boilerplate code or interface inheritance.
In the above example of a graphing system, partial application reduced our generalised 3-argument curried linear function down to a specialised 1-argument functions like diagonal. It is DRY:
steep(x) = linear(10, 5) // instead of writing 10 * x + 5
shallow(x) = linear(0.5, -5) // instead of writing 0.5 * x — 5
Furthermore, the linear function can be accurately characterised, tested, and reasoned about. The derived functions all benefit from this. It helps minimise unknowns, edge cases and errors: for example, we reduce the risk of accidentally typing a + b * x instead of a * x + b.
Performance & closures
Partial application is a language feature, and different languages may implement it differently.
The functional syntax facilitates opportunities for optimisation, like elimination of the function calls, which may not have been possible with an object model. Compiled languages may be able to perform static optimisation and remove all function calls (inlining), and interpreted languages may be able to do the same at runtime.
For example, Scala compiles to intermediate bytecode that runs on the Java Virtual Machine, and partial application is materialised using Java objects and classes. Its syntax requires an underscore _ to convert the Java method linear into a function instance (diagonal, horizontal, axis). However, optimisers can refactor it so it’s no slower than uncurried methods:
Equivalent performance of full and partially applied functions in Scala 2.10 and 2.11 (OpenJDK 7 and Oracle Java 8).cloud.highcharts.com
One performance consideration in strictly-evaluated languages (without memoised lazy evaluation) is if the configuration arguments need an expensive computation. For example:
> def func(a, b)(x) = x + longComputation(a, b)
> slow = func(1, 2)
> slow(1)
3
In this case, every time slow is called, the longComputation is performed. A workaround in these languages is to use an alternative currying syntax:
> def func(a, b) = { let c = longComputation(a, b); (x) => x + c }
> fast = func(1, 2)
> fast(1)
3In this scenario, func performs the long computation and stores it as a constant, then returns a function closure that takes the final argument x.
Context, configuration and runtime dependency injection
We just saw how partial application can be used to ‘configure’ a function:
a and b (or slope and intercept) were received as the constant configuration parameters and stored for later use.
We can even model a and b as functions that depend on x (since pure functions are constant values):
polynomial(a, b, c)(x) = a(x * x) + b(x) + c
Thus we can use functions like polynomial and diagonal in any context that supplies x, independently of how the functions were created.
Another possibility is that we can invert the order of curried arguments to separate pure concerns from side effects:
> debug(msg)(printer) = printer.println(msg)
> debugName(name, value) = debug("{name} = {value}")
> debugName("x", x)
Notice how debugName has been derived from debug without performing any side effect yet. In fact, calling debugName does not perform a side effect either. However it does capture the value of x to be printed in future. To do this, the resulting function must be invoked with its dependency (printer):
> logMessage = debugName("x", x) // no side effect yet
> logMessage(stdout) // provide the runtime dependency
x = 99Since functions are first-class values, this syntax can be used ad infinitum:
log = [debugName("x", x), debugName("y", y)]
log.foreach(stdout)
log.foreach(stderr)Of course, this is just a toy example and I don’t suggest you use this specific example in practice.
As for how to inject such dependencies without getting into a tangled mess, two common examples are the Reader Monad and implicits.
Multiple parameter lists versus Currying
In some languages, all functions are curried:
linear :: Double -> Double -> Double -> Double
linear a b x = a * x + b
> ((linear 1) 2) 1
3.0
> linear 1 2 1
3.0
It is also possible to have a syntax in-between the uncurried and curried extremes. I have already used such hybrids in my examples above, but the topic deserves an explicit mention. In some languages, a specific number of arguments can be required at once:
> def linear(a, b)(x) = a * x + b
> let f = linear(1)
Compiler Error
> let f = linear(1, 2, 1)
Compiler Error
> let f = linear(1, 2)
> f(1)
3
Some language supports ‘tuple’ syntax to achieve the same effect:
linear :: (Double, Double) -> Double -> Double
linear (a, b) x = a * x + b
> (linear (1, 2)) 1
3.0
> linear (1, 2) 1
3.0
You can even imagine a larger example with more parameter lists:
def main(config1, config2)(args)(dependency1, dependency2) = { ... }Recap: summary by example
I hope you liked this article and found it helpful. Some take-home messages:
Full application. With comma-separated parameter lists, functions are invoked with all their arguments at once:
// a single 3-argument list
> linear(a: Double, b: Double, x: Double): Double = ...
> linear(1, 2, 1)
3
Partial application. With currying, a function can be invoked with fewer than all its arguments, to return a new function:
// 2 argument lists, mixed arity
> linear(a: Double, b: Double)(x: Double): Double = ...
> f = linear(1, 2)
> f(1)
3
or:
// 3 single-argument (unary) parameter lists
> linear(a: Double)(b: Double)(x: Double): Double = ...
> f = linear(1)
> g = f(2)
> g(1)
3
> h = linear(1)(2)
> h(1)
3
Configuration:
> cleverfunction(configuration)(data) = { ... }
> configured = cleverfunction(config)
> configured(data1)
result1
> configured(data2)
result2Dependency injection:
> debug(msg)(printer) = { ... }
> log = debug("Hello world")
> log(stdout)
Hello worldNested partial application:
> linear(slope, intercept)(x) = { ... }
> horizontal(y) = linear(0, y)
> x_axis = horizontal(0)
> x_axis(1)
0
> x_axis(2)
0Other applications (no pun intended)
In Scala, currying (or rather, multiple parameter lists) can be used for purposes other than partial application.
For example, the final parameter list can receive ‘implicit’ values. A function that is syntactically partially-applied can utilise implicit dependencies:
> implicit val myPrinter = System.out
> def debug(x: String)(implicit printer: java.io.PrintStream) =
printer.println(x)
> debug("Hello world") // same as debug("Hello world")(myPrinter)
Hello world
> debug("Hello world")(System.err)
Hello world
Such functions are evaluated immediately (i.e. it is equivalent to the full application).
Also, due to Scala’s support for inheritance and subclassing, Hindley-Milner type inference is unavailable. This can create inconveniences with polymorphic functions. However, Scala’s type inference can be aided by parameter lists, since subsequent lists can infer types resolved by previous lists.
Counter-example: An alternative to currying and partial application
An object-oriented approach might look more like this:
interface SingleValuedGraphFunction {
method Double getValue(x: Double);
}class LinearGraphFunction extends SingleValuedGraphFunction {final private Double a;
final private Double b;
init LinearGraphFunction(a: Double, b: Double) {
this.a = a;
this.b = b;
} override method Double getValue(x: Double) {
return a * x + b;
}}
> LinearGraphFunction diagonal = new LinearGraphFunction(0, 1);
> diagonal.getValue(1);
1
Indeed, the language features of currying and partial application could be implemented this way at runtime. However, the syntax of currying and partial application is more concise and has automatic information hiding:
> linear(a, b)(x) = a * x + b
> diagonal = linear(1, 2)
> diagonal(1)
3
Thanks for reading, please let me know if you like this article!
Unrelated Appendix: Partial Functions
A word of warning: ‘partial functions’ and ‘partial application’ are unrelated concepts despite similar names:
- A total function is one that has a defined return value (of the right type) for every possible input.
- A partial function doesn’t.
- This issue is independent, orthogonal and unrelated to currying or partial application.
Cons of Partial Functions
The division operator in most standard libraries is effectively a partial function. Although division should take two real numbers and return a real number, the operator cannot always comply with this contract. Instead, it might throw an Exception instead of returning a value:
> 1 / 0
Exception: Division by zero
In pure functional programming, this is to be avoided. The solution is to define the return type differently. One possibility:
> 1 / 2
Success(0.5)
> 1 / 0
ArithmeticFailure
In functional programming, this is preferred because edge-case handling is encoded and enforced by the type system.
Another possibility:
> 1.0 / 2
0.5
> 1.0 / 0
Infinity
This is a shortcut that trades safety for speed or temporary convenience. It is a minefield of corner cases.
Pros of Partial Functions
Fortunately, partial functions can be put to good use as a flexible alternative to switch statements. Instead of this:
switch (value) {
case pattern1: expression1(value); break
case pattern2: expression2(value); break
default: expression3(value)
}The task can be coded functionally as:
func handler1(value) = case pattern1: expression1(value)
func handler2(value) = case pattern2: expression2(value)
func defaultHandler(value) = expression3(value)
let switchFunc = handler1 orElse handler2 otherwise defaultHandler
In this scenario, handler1 and handler2 are partial functions, while defaultHandler and switchFunc are total functions. The declaration syntax of switchFunc is analogous to function- and monad-composition. It allows the ‘sequencing’, ‘layering’ or ‘stacking’ of modular functionality.
Although the native switch syntax may seem cleaner in this small example, partial functions offer several advantages including:
- Design and maintenance: Each handler is a small unit with a single responsibility.
- Testing: Each handler can be independently unit tested. In fact, every pattern and expression can be independently unit tested.
- Reusability: Individual handlers can be reused elsewhere.
- Composability: We can create new switches from any combination of handlers we need. We can even create switches dynamically at runtime (e.g. based on configuration).
The same can be achieved with total functions, but it is not as DRY:
func handler1(value) =
case pattern1: Handled(expression1(value));
default: Unhandled
func handler2(value) =
case pattern2: Handled(expression2(value));
default: Unhandled
func defaultHandler(value) =
expression3(value)
let switchFunc = handler1 orElse handler2 otherwise defaultHandler
Here are some extended examples of this concept:
let switchFunc = securityAccessDenied orElse handleAPIRequest otherwise commandNotRecognised
let switchFunc = handleAPIv2 orElse handleAPIv1 otherwise commandNotRecognised
let switchFunc = handledPaidFeatures orElse handleFreeFeatures otherwise offerPaidFeatures
let week16Handlers = [saturdaySpecialEventHandler, normalWeekendHandler, normalWeekdayHandler]
Nifty, huh?
Anyway, hopefully you can see that ‘partial functions’ are an entirely different concept from ‘partial application’, but both are useful in their own right.