Ever heard about "currying" in the programming language paradigm? Nope, it's not an Indian recipe, but it can certainly make your code tastier.
Whether you've encountered closures and currying before or you're new to this, in this guide you'll learn about:
- The difference between closures and currying
- Currying and its main advantages
- Why you should use currying in your projects
I will give you the theory as well as valid use-cases, examples, and a solid mathematical background.
Disclaimer: I will base this article on JavaScript, however, the main idea can be applied to any modern programming language.
What are closures?
A closure may be described as a combination of a function and the scope in which it was declared.
But what exactly does this mean? The scope consists of any local variables in the function's environment when the function is created. A closure enables one to refer to all local variables of a function in the state they were found.
This is essentially achieved by defining a function inside another function: a function within a function is technically a closure. When you activate the main function (also known as a parent or outer function), you will also produce a new context of execution that keeps a fresh copy of all local variables. These local variables can be referred to in the global scope by either linking them to variables declared globally or returning the closure from the parent function.
Here's an example:
const closuredFunction= someVariable =>{
let scopedVariable=someVariable;
const closure=()=>{
scopedVariable++;
console.log(scopedVariable);
}
return closure;
}
Note: I've assumed a someVariable
as an integer (because of ++
), but it can be extrapolated to any other type of variable. I will keep using arrow functions throughout this article, if you need further explanations just post a comment and I will refactor it.
Closures: practical application
Before the introduction of classes in ES6, closures represented a simple way of creating class-like privacy comparable to the one used in OOP (Object Oriented Programming), allowing to emulate private methods. This is known as the "module pattern" and it allows us to write easily maintainable code with reduced namespace pollution and more reusability.
Continuing with the above example, the outer function (closuredFunction
) is a public function that has access to some private variables (scopedVariable
) and the inner functions (closure
).
Now, let's apply the following:
const closuredFunction= someVariable =>{
let scopedVariable=someVariable;
const closure=()=>{
scopedVariable++;
console.log(scopedVariable);
}
return closure;
}
let testOne = closuredFunction(1);
testOne(); // will output 2
testOne(); // will output 3
let testTwo = closuredFunction(10);
testTwo(); // will output 11
testTwo(); // will output 12
testOne(); // will output 4
What is happening? All the invocations of testOne()
are accessing the same outer scope, therefore, the same scopedVariable
. If one changes, the next value will change accordingly.
However, another call of the same outer function creates a new scope, with a new scopedVariable
, so an entirely independent variable will be prompted, because of the scope and the context.
What is currying?
Curry is a variety of dished originating in the Indian subcontinent that use a complex combination of spices and herbs.
Ok, one curry joke per article is enough.
Currying refers to the process of transforming a function with multiple arity into the same function with less arity. The curried effect is achieved by binding some of the arguments to the first function invoke, so that those values are fixed for the next invocation. - Kristina Brainwave
Or in other words:
Currying is a pattern of functions that instantly evaluate and return other functions. This can work because JavaScript functions are expressions that can return other functions as we saw in the previous section (closures).
Curried functions are constructed by chaining closures and immediately returning their inner functions simultaneously.
How to use currying
A standard function call may look like this:
sampleFunction('param1', 'param2', 'param3');
A curried function may look like this:
sampleFunction('param1')('param2')('param3');
If this looks familiar it's indeed because a HOC (High-Order Component) is a curried function.
Translating the previous snippet into a curried function will be:
function sampleFunction(param1){
//some logic
return param2 => {
//some other logic
return param3 => {
return finalResult;
}
}
}
The final function in the chain has access to all the arguments in the chain. The key with curried functions is that you still have access to the functions inside the curried function.
How?
let someParam = sampleFunction(param1);
let anotherParam = someParam(param2);
console.log(anotherParam(param3));
Or in an unrefined way:
sampleFunction('param1')('param2')('param3');
This means that regardless of how it's called, as long as the sequence is correct, the final result will always return as expected.
Is currying a form of closure?
Yes, as you may have noticed by now, the two patterns share similarities. However, they have different use-cases.
Currying means that the closure does not have to receive all of its arguments at once, but separately.
I've found this useful metaphor around the internet:
Think of currying as adding ingredients (arguments, or other spices) to a function one by one. You can drop some arguments now, and other arguments as you go. This can be useful if the arguments in your function depend on other actions in the program. But also if you want to make a closure with one argument of a function, and then curry the second argument if that argument will be a different value each time you call it.
What are the benefits of currying? When I should use it?
As your project grows, you'll soon realize how useful currying can be, as it's extremely scalable. The more complex the project, the more currying will turn out to be a lifesaver.
If you want to keep control of large and complex applications, you can turn to currying as a safe methodology to ensure a clean code.
Currying and clean code.
Imagine that you start compiling a long list of functions. Clean code best practice teaches you that you need all functions to operate independently and remain as simple and atomized as possible to avoid side effects.
Side effects are what happens, for example, when you place a whole lot of messy code within one function, effectively affecting what other functions are doing further down the line without respecting the scope and modifying the variable's state. In other words, a side effect is an operation, function, or expression that modifies some state variable value(s) outside its local environment.
If your functions are overcharged (as to say, if you have lots of side effects), your code isn't light and clean. And if your code isn't clean, your project is unscalable and hard to maintain.
Ideally, functions should receive as little as 1 parameter.
Closures fell in popularity since JavaScript incorporated classes in ES6. However, closures and currying can still be a crucial part of a clean, scalable code. In functional programming, they essentially serve a similar purpose to private methods in Object Oriented Programming.
Now you know what closures and currying are, how to use them, and why. Most people can stop here and go code but if you're a bit of a weirdo like me, here's an extra mathematical treat from Wikipedia for your entertainment.
Mathematical background
In mathematics and computer science, currying is the technique of converting a function that takes multiple arguments into a sequence of functions that each take a single argument. For example, currying a function
Or more abstractly, a function that takes two arguments, one from X and one from Y, and produces the Z output by currying is translated into a function that takes a single argument from X and produces as outputs functions from X to Z.
Currying provides a way for working with functions that take multiple arguments and using them in frameworks where functions might take only one argument. For example, some analytical techniques can only be applied to functions with a single argument. Practical functions frequently take more arguments than this. Frege showed that it was sufficient to provide solutions for the single argument case, as it was possible to transform a function with multiple arguments into a chain of single-argument functions instead. This transformation is the process now known as currying. All "ordinary" functions that might typically be encountered in mathematical analysis or in computer programming can be curried. However, there are categories in which currying is not possible; the most general categories which allow currying are the closed monoidal categories.
Currying is related to, but not the same as partial application. In practice, the programming technique of closures can be used to perform partial application and a kind of currying, by hiding arguments in an environment that travels with the curried function, as we previously saw.
These are some core concepts of functional JavaScript programming and can be used to make your code flexible and powerful. If you have any real-life examples of currying, or a favorite curry dish, feel free to curry it into the comments.
Top comments (0)