Currying, or partial application, is one of the functional techniques that can sound confusing to people familiar with more traditional ways of writing JavaScript. But when applied properly, it can actually make your functional JavaScript more readable.
Key Takeaways
- Currying is a functional programming technique that allows partial application of a function’s arguments, meaning you can pass all or a subset of the arguments a function is expecting. If a subset is passed, a function is returned that awaits the remaining arguments.
- Currying can make JavaScript code more readable and flexible. It allows the creation of a library of small, easily configured functions that behave consistently, are quick to use, and can be understood when reading your code.
- The order of arguments is important when currying. The argument that is most likely to be replaced from one variation to another should be the last argument passed to the original function.
- Some functional JavaScript libraries, such as Ramda, offer flexible currying functions that can break out the parameters required for a function, and allow you to pass them individually or in groups to create custom curried variations. If you plan to use currying extensively, using such libraries is recommended.
More Readable And More Flexible
One of the advantages touted for functional JavaScript is shorter, tighter code that gets right to the point in the fewest lines possible, and with less repetition. Sometimes this can come at the expense of readability; until you’re familiar with the way the functional programming works, code written in this way can be harder to read and understand.
If you’ve come across the term currying before, but never knew what it meant, you can be forgiven for thinking of it as some exotic, spicy technique that you didn’t need to bother about. But currying is actually a very simple concept, and it addresses some familiar problems when dealing with function arguments, while opening up a range of flexible options for the developer.
What Is Currying?
Briefly, currying is a way of constructing functions that allows partial application of a function’s arguments. What this means is that you can pass all of the arguments a function is expecting and get the result, or pass a subset of those arguments and get a function back that’s waiting for the rest of the arguments. It really is that simple.
Currying is elemental in languages such as Haskell and Scala, which are built around functional concepts. JavaScript has functional capabilities, but currying isn’t built in by default (at least not in current versions of the language). But we already know some functional tricks, and we can make currying work for us in JavaScript, too.
To give you a sense of how this could work, let’s create our first curried function in JavaScript, using familiar syntax to build up the currying functionality that we want. As an example, let’s imagine a function that greets somebody by name. We all know how to create a simple greet function that takes a name and a greeting, and logs the greeting with the name to the console:
var greet = function(greeting, name) {
console.log(greeting + ", " + name);
};
greet("Hello", "Heidi"); //"Hello, Heidi"
This function requires both the name and the greeting to be passed as arguments in order to work properly. But we could rewrite this function using simple nested currying, so that the basic function only requires a greeting, and it returns another function that takes the name of the person we want to greet.
Our First Curry
var greetCurried = function(greeting) {
return function(name) {
console.log(greeting + ", " + name);
};
};
This tiny adjustment to the way we wrote the function lets us create a new function for any type of greeting, and pass that new function the name of the person that we want to greet:
var greetHello = greetCurried("Hello");
greetHello("Heidi"); //"Hello, Heidi"
greetHello("Eddie"); //"Hello, Eddie"
We can also call the original curried function directly, just by passing each of the parameters in a separate set of parentheses, one right after the other:
greetCurried("Hi there")("Howard"); //"Hi there, Howard"
Why not try this out in your browser?
Curry All the Things!
The cool thing is, now that we have learned how to modify our traditional function to use this approach for dealing with arguments, we can do this with as many arguments as we want:
var greetDeeplyCurried = function(greeting) {
return function(separator) {
return function(emphasis) {
return function(name) {
console.log(greeting + separator + name + emphasis);
};
};
};
};
We have the same flexibility with four arguments as we have with two. No matter how far the nesting goes, we can create new custom functions to greet as many people as we choose in as many ways as suits our purposes:
var greetAwkwardly = greetDeeplyCurried("Hello")("...")("?");
greetAwkwardly("Heidi"); //"Hello...Heidi?"
greetAwkwardly("Eddie"); //"Hello...Eddie?"
What’s more, we can pass as many parameters as we like when creating custom variations on our original curried function, creating new functions that are able to take the appropriate number of additional parameters, each passed separately in its own set of parentheses:
var sayHello = greetDeeplyCurried("Hello")(", ");
sayHello(".")("Heidi"); //"Hello, Heidi."
sayHello(".")("Eddie"); //"Hello, Eddie."
And we can define subordinate variations just as easily:
var askHello = sayHello("?");
askHello("Heidi"); //"Hello, Heidi?"
askHello("Eddie"); //"Hello, Eddie?"
Currying Traditional Functions
You can see how powerful this approach is, especially if you need to create a lot of very detailed custom functions. The only problem is the syntax. As you build these curried functions up, you need to keep nesting returned functions, and call them with new functions that require multiple sets of parentheses, each containing its own isolated argument. It can get messy.
To address that problem, one approach is to create a quick and dirty currying function that will take the name of an existing function that was written without all the nested returns. A currying function would need to pull out the list of arguments for that function, and use those to return a curried version of the original function:
var curryIt = function(uncurried) {
var parameters = Array.prototype.slice.call(arguments, 1);
return function() {
return uncurried.apply(this, parameters.concat(
Array.prototype.slice.call(arguments, 0)
));
};
};
To use this, we pass it the name of a function that takes any number of arguments, along with as many of the arguments as we want to pre-populate. What we get back is a function that’s waiting for the remaining arguments:
var greeter = function(greeting, separator, emphasis, name) {
console.log(greeting + separator + name + emphasis);
};
var greetHello = curryIt(greeter, "Hello", ", ", ".");
greetHello("Heidi"); //"Hello, Heidi."
greetHello("Eddie"); //"Hello, Eddie."
And just as before, we’re not limited in terms of the number of arguments we want to use when building derivative functions from our curried original function:
var greetGoodbye = curryIt(greeter, "Goodbye", ", ");
greetGoodbye(".", "Joe"); //"Goodbye, Joe."
Getting Serious about Currying
Our little currying function may not handle all of the edge cases, such as missing or optional parameters, but it does a reasonable job as long as we stay strict about the syntax for passing arguments.
Some functional JavaScript libraries such as Ramda have more flexible currying functions that can break out the parameters required for a function, and allow you to pass them individually or in groups to create custom curried variations. If you want to use currying extensively, this is probably the way to go.
Regardless of how you choose to add currying to your programming, whether you just want to use nested parentheses or you prefer to include a more robust carrying function, coming up with a consistent naming convention for your curried functions will help make your code more readable. Each derived variation of a function should have a name that makes it clear how it behaves, and what arguments it’s expecting.
Argument Order
One thing that’s important to keep in mind when currying is the order of the arguments. Using the approach we’ve described, you obviously want the argument that you’re most likely to replace from one variation to the next to be the last argument passed to the original function.
Thinking ahead about argument order will make it easier to plan for currying, and apply it to your work. And considering the order of your arguments in terms of least to most likely to change is not a bad habit to get into anyway when designing functions.
Conclusion
Currying is an incredibly useful technique from functional JavaScript. It allows you to generate a library of small, easily configured functions that behave consistently, are quick to use, and that can be understood when reading your code. Adding currying to your coding practice will encourage the use of partially applied functions throughout your code, avoiding a lot of potential repetition, and may help get you into better habits about naming and dealing with function arguments.
If you enjoyed, this post, you might also like some of the others from the series:
- An Introduction to Functional JavaScript
- Higher-Order Functions in JavaScript
- Recursion in Functional JavaScript
Frequently Asked Questions (FAQs) about Currying in Functional JavaScript
What is the main difference between currying and partial application in JavaScript?
Currying and partial application are both techniques in JavaScript that allow you to pre-fill some of the arguments of a function. However, they differ in their implementation and usage. Currying is a process in functional programming where a function with multiple arguments is transformed into a sequence of functions, each with a single argument. On the other hand, partial application refers to the process of fixing a number of arguments to a function, producing another function of smaller arity. While currying always produces nested unary (1-arity) functions, partial application can produce functions of any arity.
How does currying enhance code readability and maintainability in JavaScript?
Currying can significantly enhance code readability and maintainability in JavaScript. By breaking down complex functions into simpler, unary functions, currying makes the code more readable and easier to understand. It also promotes cleaner and more modular code, as each function performs a single task. This modularity makes the code easier to maintain and debug, as issues can be isolated to specific functions.
Can you provide an example of currying in JavaScript?
Sure, let’s consider a simple example of a function that adds three numbers. Without currying, the function might look like this:function add(a, b, c) {
return a + b + c;
}
console.log(add(1, 2, 3)); // Outputs: 6
With currying, the same function would be written as:function add(a) {
return function(b) {
return function(c) {
return a + b + c;
}
}
}
console.log(add(1)(2)(3)); // Outputs: 6
What are the limitations or drawbacks of using currying in JavaScript?
While currying has its benefits, it also has some limitations. One of the main drawbacks is that it can make the code harder to understand for those not familiar with the concept, especially when dealing with functions with a large number of arguments. It can also lead to performance overhead due to the creation of additional closures. Furthermore, it can make function calls more verbose, as each argument must be passed in a separate set of parentheses.
How does currying relate to higher-order functions in JavaScript?
Currying is closely related to the concept of higher-order functions in JavaScript. A higher-order function is a function that takes one or more functions as arguments, returns a function as its result, or both. Since currying involves transforming a function into a sequence of function calls, it inherently involves the use of higher-order functions.
Can currying be used with arrow functions in JavaScript?
Yes, currying can be used with arrow functions in JavaScript. In fact, the syntax of arrow functions makes them particularly well-suited for currying. Here’s how the previous add function could be written using arrow functions:const add = a => b => c => a + b + c;
console.log(add(1)(2)(3)); // Outputs: 6
Is currying used in popular JavaScript libraries or frameworks?
Yes, currying is used in several popular JavaScript libraries and frameworks. For example, it’s a fundamental concept in libraries like Ramda and Lodash, which provide utility functions for functional programming in JavaScript. It’s also used in Redux, a popular state management library for React.
How does currying help with function composition in JavaScript?
Currying can be very helpful when it comes to function composition in JavaScript. Function composition is the process of combining two or more functions to create a new function. Since currying allows you to create unary functions, it simplifies function composition by ensuring that each function has exactly one input and one output.
Can all JavaScript functions be curried?
In theory, any JavaScript function with two or more arguments can be curried. However, in practice, it may not always be practical or beneficial to curry a function. For example, currying might not be useful for functions that need to be called with different numbers of arguments at different times.
How does currying affect the performance of JavaScript code?
While currying can make your code more readable and modular, it can also have a slight impact on performance. This is because every time you curry a function, you create new closures. However, in most cases, the impact on performance is negligible and is outweighed by the benefits of improved code readability and maintainability.
I've worked as a Web Engineer, Writer, Communications Manager, and Marketing Director at companies such as Apple, Salon.com, StumbleUpon, and Moovweb. My research into the Social Science of Telecommunications at UC Berkeley, and while earning MBA in Organizational Behavior, showed me that the human instinct to network is vital enough to thrive in any medium that allows one person to connect to another.