Note: This is a part of the series about functional programming in JavaScript. Check the theoretical introduction to FP and part I about point-free style and argument adapters.
Partial application
Partial application is a process of reducing a function’s arity (the number of its parameters) by creating a new function with some of the arguments passed in. Before seeing how it works internally, let’s see a practical use case. Think of the following situation: you’ve got some mapper function that you would like to use on a few different arrays. Classically, you would need to do something like this:
const double = number => number * 2;
[1, 2, 3].map(double); // [2, 4, 6]
[4, 5, 6].map(double); // [8, 10, 12]
Now, let’s use Ramda’s map instead of the built-in one. Important note: Ramda’s map takes a mapper as the first argument and the array as the second.
import { map } from 'ramda';
map(double, [1, 2, 3]); // [2, 4, 6]
map(double, [4, 5, 6]); // [8, 10, 12]
You may wonder why we’d use a custom function instead of the built-in one, but bear with me here - the more we get into the functional patterns, the more clear it will be why it’s so useful.
Now, by using partial, we can make the code samples above more DRY:
import { map, partial } from 'ramda';
const doubleEvery = partial(map, [double]);
doubleEvery([1, 2, 3]); // [2, 4, 6]
doubleEvery([4, 5, 6]); // [8, 10, 12]
Pretty cool, right? Partial takes the function we want to partially apply as the first argument, then an array of arguments that we want to apply and returns a new function as a result. Also, notice how the usage of a custom map makes partial application trivial here.
What if we wanted to do the opposite, and do a few different mapping operations on the same array? We could use the flip function (reverses the order of the first two arguments passed in) we talked about in the previous article:
import { flip, map, partial } from 'ramda';
const double = n => n * 2;
const triple = n => n * 3;
const mapNumbers = partial(flip(map), [[1, 2, 3]]);
mapNumbers(double); // [2, 4, 6]
mapNumbers(triple); // [3, 6, 9]
Okay, it works, but wouldn’t it be cool if we could simplify it a bit more? Luckily, Ramda has us covered - there’s a partialRight function which suits our needs perfectly. Here it is in action:
import { map, partialRight } from 'ramda';
const double = n => n * 2;
const triple = n => n * 3;
const mapNumbers = partialRight(map, [[1, 2, 3]]);
mapNumbers(double); // [2, 4, 6]
mapNumbers(triple); // [3, 6, 9]
The idea here is the same as before, we just apply the arguments starting from the right side.
Implementation
Let’s get a bit deeper and implement partial and partialRight ourselves. Here’s a simple application of the former:
const partial = (fn, args) => (...nextArgs) => fn(...args, ...nextArgs);
Simple, right? We take a function and an array of arguments. This function returns another function which gathers all the other arguments into a second array (nextArgs). Then, it calls the original function fn with both of the argument arrays spread out.
How about partialRight? We can just spread the nextArgs before args:
const partialRight = (fn, args) => (...nextArgs) => fn(...nextArgs, ...args);
Currying
If you’ve read the previous part of the series, you may remember that we have already mentioned (and used!) currying. It’s time to finally explain what it’s about. And if you’re wondering this pattern has such a strange name, here’s the reason.
So what is currying? As Kyle Simpson puts it in his book: “currying unwinds a single higher-arity function into a series of chained unary functions”.
I know it sounds scary, so let’s look at a quick example and write a function that adds three numbers using currying. We’ll use the good old function keyword for readability’s sake.
function addThree(a) {
return function(b) {
return function(c) {
return a + b + c;
}
}
}
addThree(2)(4)(6) // 12
Notice how we keep returning functions, as long as the last parameter is specified, and then we calculate and return the result.
Let’s also consider our doubleEvery example, using curry instead of partial and breaking it down to simple steps:
import { curry, map } from 'ramda';
const double = n => n * 2;
const curriedMap = curry(map);
const doubleEvery = curriedMap(double);
doubleEvery([1, 2, 3]); // [2, 4, 6]
Now, let’s shorten it a bit by defining doubleEvery as a one-liner:
import { curry, map } from 'ramda';
const double = n => n * 2;
const doubleEvery = curry(map)(double);
doubleEvery([1, 2, 3]); // [2, 4, 6]
What curry does is taking the original function (map in this case) and transforming it into a series of functions (classically) unary functions, that will return the result when all of the arguments have been satisfied. While the classic approach requires every produced function to be unary, Ramda’s implementation provides a more flexible utility, allowing us to specify more than one parameter at a time: fn(arg1)(arg2, arg3)..., etc.
Currying is becoming more and more popular in the community. It is becoming widely used in some of the popular libraries. For example, if you’ve ever used Redux with React (or the react-redux package in particular), you’ve used currying without even knowing it:
export default connect(mapStateToProps)(MyComponent);
Implementation
Now, we will write a simple implementation of curry ourselves. Bear in mind - simple is an important word here - the implementation will be flawed.
const curry = fn => {
return function nextCurry(...args) {
if (args.length < fn.length) { // branch 1
return (...nextArgs) => nextCurry(...args, ...nextArgs);
}
return fn(...args); // branch 2
}
}
Looks a bit harder to grasp than partial, but it’s not so bad. So how does it work? First, it takes the original fn as an argument. Then it returns another function, nextCurry that gathers all the arguments.
Quick question: why did we use a named function here, instead of just using an anonymous one? It’s because we need a named function for the recursive call in the first branch.
As you can see, nextCurry has two branches. The first one is executed if the number of the arguments passed in is lesser than the arity of fn (it’s not so commonly known that we can get the arity of a function by accessing its length property). It returns yet another function that gathers nextArgs and calls nextCurry again, spreading both the previous args and nextArgs as its arguments. If all arguments are satisfied (second branch), we just call the original function with them.
This implementation works quite nice, even when passing a few (or all) arguments at a time. It has some serious issues though. It won’t work when the original function has the rest operator in its signature or uses default parameters. In other words - it works only when the original function’s arity can be determined easily. We could get around that problem using Ramda’s curryN that takes the arity explicitly as a first parameter. It will also break when using default parameters and passing a few arguments at once (unlike Ramda’s implementation, which will work).
Quick side note: implementing all sorts of utilities yourself is a great exercise. Despite that, I would advise against using them in production (unless pulling a third-party library is not an option for some reason). Instead, I’d recommend using any battle-tested library of your choice, be it Ramda, Lodash/FP, or any other popular tool, as they’re well tested and widely used by the community, making them a lot less prone to errors. And if you’re worried that pulling the entirety of Lodash or Ramda may bloat your bundle size, you could take advantage of tree shaking, for example by cherry-picking imported methods or, even easier, using tools like babel-plugin-ramda.
Which one should I choose?
At this point, you may wonder if you should prefer partial application or currying. Both approaches seem (and work) quite familiar, allowing us to move from generalized functions to more specialized ones.
Currying is generally more popular than partial application in FP. Transforming a function into a series of functions makes the original function very flexible, as we can apply any number of arguments we want at a given time. It can also save us some keystrokes, as, for example, applying all arguments to a function one at a time with the partial application would require calling partial every time. Moreover, currying will become immensely helpful when we start using composition (coming up in the next part of the series!).
On the other hand, partial (or partialRight in this case) can be extremely useful when we want to apply some parameters from the right-hand side. We could theoretically try some weird shenanigans with curry, combining it with something like reverseArgs, but that could be troublesome and not really worth the hassle.
So, the answer to our question - there is no clear winner, and that’s good. Both patterns can be extremely helpful, and you should always choose the one that is best in a given situation.
Coming up next in functional programming in JavaScript series
So far we have learned a few great functional programming patterns. But in the next part, we will learn about a true game-changer - composition. We’ll be using argument adapters, point-free style, partial application, currying, and composition together, so be sure to get comfortable with these concepts if you’re not already!
Like what our developers do? Join our frontend team, we’re looking for software developers - check our job offers!
Navigate the changing IT landscape
Some highlighted content that we want to draw attention to to link to our other resources. It usually contains a link .