Currying is a fundamental tool in functional programming, a programming pattern that tries to minimize the number of changes to a program’s state (known as side effects) by using immutable data and pure (no side effects) functions.
Currying
Currying is the process of taking a function with multiple arguments and returning a series of functions that take one argument and eventually resolve to a value.
Here’s a simple example to illustrate it using the _.curry function from Lodash (we’ll build our own curry function shortly):
function volume(l, w, h) {
return l * w * h;
}
const curried = _.curry(volume);
volume(2, 3, 4); // 24
curried(2)(3)(4); // 24
The original function
volume
takes three arguments, but once curried we can instead pass in each argument to three nested functions.
In other words, currying has effectively done this:
function volume1(length) {
return function(width) {
return function(height) {
return height * width * length;
}
}
}
volume1(2)(3)(4); // 24
If you’re confused how the innermost function has access to the variables in outer functions, learn more about closures and scoping in JavaScript.
Currying? What for?
To understand the benefits we need a worthy real-life example.
For instance, we have the logging function
log(date, importance, message)
that formats and outputs the information. In real projects such functions have many useful features like sending logs over the network, here we’ll just use alert
:function
log
(
date,
importance,
message
)
{
alert
(
`[
${
date.
getHours
(
)
}
:
${
date.
getMinutes
(
)
}
] [
${
importance}
]
${
message}
`
)
;
}
Let’s curry it!
log =
_.
curry
(
log)
;
After that
log
works normally:log
(
new
Date
(
)
,
"DEBUG"
,
"some debug"
)
;
// log(a, b, c)
…But also works in the curried form:
log
(
new
Date
(
)
)
(
"DEBUG"
)
(
"some debug"
)
;
// log(a)(b)(c)
Now we can easily make a convenience function for current logs:
// logNow will be the partial of log with fixed first argument
let
logNow =
log
(
new
Date
(
)
)
;
// use it
logNow
(
"INFO"
,
"message"
)
;
// [HH:mm] INFO message
Now
logNow
is log
with the fixed first argument, in other words, “partially applied function” or “partial” for short.
We can go further and make a convenience function for current debug logs:
let
debugNow =
logNow
(
"DEBUG"
)
;
debugNow
(
"message"
)
;
// [HH:mm] DEBUG message
So:
- We didn’t lose anything after currying:
log
is still callable normally. - We can easily generate partial functions such as for today’s logs.