If you are new to software development you've probably come across the term currying and still ask yourself what the h*ck that is.
Let's start with the main concept:
In mathematics and computer science, "currying" is the technique of converting a function that takes multiple arguments into a sequence of functions that each takes a single argument.
Source: Wikipedia
If this still doesn't make sense to you, don't worry! Let's dive into some practical examples and you'll figure it out.
TL;DR
In this article I explain, in a practical way, what currying is within a programming context and how to take advantage of it.
The examples are all in JavaScript but they may be applied in several other languages.
It's important to know that currying does not call a function, but transforms it. Basically, it turns this fn(param1, param2, param3)
into this fn(param1)(param2)(param3)
.
It's very common to find out there currying definitions and examples that, in fact, refer to partial function application.
If you, like me, Are interested in functional programming but are (still) not an expert in any purely functional language, these two concepts may be confused.
Partial Application
In functional languages, functions are not "invoked" but "applied" to their arguments. They are known as "first-class citizens", which means they can be assigned to variables and interact with other functions being used as parameters or even being returned by them.
A function is partially applied when it is applied to only part of its parameters and returns another function that expects the other ones.
For example:
function sum(num1, num2, num3) {
return num1 + num2 + num3
}
// It can be invoked in two different ways
sum(1, 2, 3) // 6
sum.appply(null, [1, 2, 3]) // 6
Now, let's create a function that applies sum
partially, which means, it fixes the first parameter and returns a function that expects the other two.
function partialSum(num1) {
return function(num2, num3) {
return sum(num1, num2, num3)
}
}
// Using it
const sumTwo = partialSum(2)
sumTwo(3, 4) // 9
This operation may even be generalized to any function:
function partial(fn) {
const args = Array.prototype.slice.call(arguments, 1)
return function() {
return fn.apply(null, args.concat(Array.prototype.slice.call(arguments, 0)))
}
}
// Using it
const sumTwo = partial(sum, 2)
sumTwo(3, 4) // 9
Currying
According to currying most precise definition (as mentioned at the beginning of this article):
"currying" is the technique of converting a function that takes multiple arguments into a sequence of functions that each takes a single argument
Notice that the example above does not match the definition requirements because partial(sum, 2)
returns a function that deals with two arguments instead of one.
If our function did currying, it would have to be called like this to get the same final result:
const sumTwo = curry(soma, 2)
sumTwo(3)(4) // 9
Its utility is arguable not only in JavaScript but other languages as well. There are some justifiable use cases but some simply are not.
Currying is very useful when functions are always unary, which means they only accept one argument.
Examples
Enough talking, let's see some real-life examples of currying:
Notice that these examples are just to explain a little bit better how to implement curry, if they are useful or not is not being discussed here
Imagine you have a function responsible for logging activities within your application. Your logs may have 3 parameters and you'd like to curry it so that it becomes more flexible.
const logFn = (timestamp, type, message) =>
console.log(`[${date.getHours()}:${date.getMinutes()}] [${type}] ${message}`)
To curry the function above we could implement our own currying function or take advantage of any library that does this for us. In this case, we're going to use Lodash Functional Programming package.
It does the heavy work for us and saves us time.
(If you want to try your own currying implementation, check this piece of code)
Currying our log function:
const { curry } from 'lodash/fp'
const log = curry(logfn)
After this, we can call our function normally:
const timestamp = new Date()
const type = 'WARNING'
const message = 'Some warning'
log(timestamp, type, message)
But we can also call it in the curried form:
const timestamp = new Date(
const type = 'WARNING'
const message = 'Some warning'
log(time)(type)(message)
And we can easily make a convenience function for current logs:
const logNow = log(new Date().getTime())
// Usage
logNow('INFO', 'Message') // [HH:mm] INFO Message
Another common use for curried functions is when you deal with chained functions as when using pipe from Lodash, for example.
But this is something for another article :)
Conclusion
Currying is a little bit complicated to understand at first glance, but if you exercise it every day, you will figure it out and, most important, understand when to use it because, at the end of the day, this is what really matters: knowing which technique must be applied to each scenario.
I hope you liked it.
Comment and share!
P.S.: Feel free to correct me if I'm wrong at some point.
Currying is still a little bit new to me :)
Top comments (0)