In this blog the reader would learn about the basics of currying
in Javascript. The excitement which comes on hearing the curry food
is coming in soon ๐ so is my excitement in explaining the basics of currying and how it is implemented in Vanilla JS. Let's get going ๐.
Introduction:
As per several references, currying can be seen as one of the new ways to write functions. Basically it involves transformation of a function that takes several parameters into a series of smaller functions that take lesser number of parameters. In other words if we have a function add
that requires the use of 3 parameters to compute the sum of those 3 parameters then after applying currying that same function would require the use of those 3 parameters but in a different fashion. Let's see it below
function add(a, b, c) {
return a + b + c;
}
let sumOf234 = add(2, 3, 4)
console.log(sumOf234) //10
This is a simple function that we wrote to compute the sum of 3 numbers. Now let's use currying to rewrite this add function.
function add(firstNum) {
return function(secondNum) {
return function(thirdNum) {
return firstNum + secondNum + thirdNum;
}
}
}
As you can see we have started breaking the function to take lesser number of parameters and generate new functions that have their parameter requirements which further compute the results.
Now you might be wondering that how the innermost function might have access to the parameters passed to the outer functions. That brings us to another concept called as Closures which explains about the innermost function's access to the outside environment. The definition from MDN can be seen as:
A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function's scope from an inner function.
Here is an example demonstrating closures
//
function makeFunc() {
const name = 'Currying';
function displayName() {
console.log(name);
}
return displayName;
}
const myFunc = makeFunc();
myFunc();
//as soon as we invoke myFunc,
//the function would get executed
// we would get to see Currying on the console
So once the currying function gets made how can we invoke it or call it to receive the value? There are two ways to do so:
- One is by directly passing the parameters to the main function. For example in this case:
console.log(add(2)(3)(4));
//4
- Other is by creating temporary variables that store the functions and pass the parameters one by one while making them. This example might help you understand:
const add2=add(2);
// add2 is a variable which stores a function
// which will take 2 parameters further to return the result
// In case you want to invoke it directly can write this
// add2(3)(4);
// else you can create the function expressions more to use it in simple format
const add23=add(2)(3);
// add23 is a variable which stores a function that will take 1 parameter further
// to return the result
const result=add(2)(3)(4)
These small functions stored within variables from the original functions are also called as partially curried functions.
Implementing currying through arrow functions becomes much more easier to understand. Here is how it looks after using arrow functions:
const add = num1=>num2=>num3=>num1+num2+num3;
consoel.log(add(2)(3)(4))
// Would work the same as above
Till now you must have got a clear idea of how to implement currying if required in the functions. Like while making the add function (can see the code snippet example where partially curried functions are stored in variables):
As soon as the add
function is invoked for the first time then it stores a functional wrapper that takes secondNum
as parameter inside the variable add2
and in the same way the variable add23
stores a functional wrapper that would take thirdNum
as parameter and give us the value of the sum.
APPLICATIONS OF CURRYING:
Suppose if we have multiple functions that take the same number of parameters and return different results based on the actions executed in those functions, then instead of calling those functions one by one by as well as passing the arguments inside it every time the functions are called is there a way to pass all the functions at once and even provide the arguments to it thus getting the result instantly?
Yes there is a way and that can be achieved through Currying.
Let's first understand through a example what I am trying to do as explained above. Suppose I have 3 functions that perform the mathematical operations of addition, subtraction & multiplication of 3 numbers which are passed to it as parameters.
Now if I want to execute a particular mathematical operation on all the 3 numbers and pass its result as one of the parameters with the remaining 2 numbers as parameters to the functions which are called afterwards;
Here is the code of all the functions indicating the mathematical operations:
// Mathematical operations written as functions to be executed on 3 numbers
const addition = (num1,num2,num3)=>num1+num2+num3;
const subtraction = (num1,num2,num3)=>num1-num2-num3;
const multiplication = (num1,num2,num3)=>num1*num2*num3;
As explained , if one of the functions is being executed first on all the 3 numbers and its return value is passed to the other functions with the remaining 2 numbers to the functions yet to be called.
// Calling the functions one by one
// with the first function's return value passed to the second function call
// which is further being passed to the final function
const sumResult = addition(2,4,5);
const differenceResult = subtraction(addResult,4,5);
const productResult = multiplication(differenceResult,4,5);
console.log(productResult);
//40
There is another way of executing the function call which looks difficult to understand at first but will get clear
const cumulativeResult=multiplication(subtraction(addition(2,4,5),4,5),4,5);
console.log(cumulativeResult);
// Using the result of one function and passing it as an argument to the corresponding
// function calls
Now this way might seem shorter of using the result of one function and passing it as an argument to the other function but not much readable and easy to understand to others.
That is where currying can assist us in simplifying the process of calling these functions together and pass the parameters all at once maintaining the readability. It lets us perform function composition
i.e. providing a set of functions that will be called in sequence by passing the results of one function as an argument to the next function. Let's witness the magic here.
const generateResult = (...participatingFunctions) => (a, b, c) => {
let value = c;
for (let i = 0; i < participatingFunctions.length; i++) {
value = participatingFunctions[i](value, a, b);
}
return value;
};
console.log("Currying and composition together: ",generateResult3(add, subtract, multiply)(2, 4, 5));
We have created a function called as generateResult
that will first take the functions which will operate on the numbers one by one and returns us a function that will take the numbers on which the mathematical operations will be carried out accordingly.
This is one of the applications of currying to perform function composition too.Real life examples of this can be seen in an:
- Ecommerce marketplace: We have a page on the ecommerce application that displays us the list of products available on the platform. In order to find our desired product we have different types of filters and sorting facilities available. On marking/unmarking those filters and sorts we get the desired product. Function composition can be used here to take all the different types of sorting and filtering functions and apply it on the products data depending on the sorts and filters applied here