An Interest In:
Web News this Week
- May 1, 2024
- April 30, 2024
- April 29, 2024
- April 28, 2024
- April 27, 2024
- April 26, 2024
- April 25, 2024
Understanding Function Currying in JavaScriptand When to Use It
In this article, we’ll discuss function currying in JavaScript, which is an advanced concept in functional programming.
JavaScript is one of the core technologies of the web. The majority of websites use it, and all modern web browsers support it without the need for plugins. In this series, we’re discussing different tips and tricks that will help you in your day-to-day JavaScript development.
What is Function Currying?
Function currying is an advanced technique for working with JavaScript functions. In fact, it’s not limited to JavaScript, it’s also used in other programming languages as well.
As per Wikipedia:
Currying is the technique of converting a function that takes multiple arguments into a sequence of functions that each take a single argument.
In other words, currying is just transformation of a function that takes multiple arguments into a sequence of nested functions that take a single argument. For example, for a function f
that takes three arguments, you would call it like f(arg1, arg2, arg3)
. When you use function currying, you would be able to call it like f(arg1)(arg2)(arg3)
.
Let’s assume that you’ve a function that takes three arguments as shown in the following snippet.
function fooBar(arg1, arg2, arg3) {
…
}
To call the above function, you would use the following syntax.
fooBar(1,2,3);
Now, let’s see a simplistic implementation of a curried version of the above function:
function fooBarCurriedVersion(arg1) {
return (arg2) => {
return (arg3) => {
return fooBar(arg1, arg2, arg3)
}
}
}
And with that, you can call the fooBarCurriedVersion
function with the following syntax:
fooBarCurriedVersion(arg1)(arg2)(arg3);
One advantage of this is that you can separate passing each argument to the function. For example, if you only know the value of arg1
at some point in the code, you could call the curried function with just that argument and pass the resulting function on to the rest of your code.
Let’s try to understand how it’s executed.
Firstly, the fooBarCurriedVersion(arg1)
statement is executed, and it returns the callable which takes a single argument. Next, the callable function which is returned by the fooBarCurriedVersion
function is called with the arg2
argument, and it again returns the callable which takes a single argument. Finally, the callable function returned by the previous step is called with the arg3
argument.
It’s important to note that since all functions are closures, argument values that are passed are preserved in between function calls. That is to say if you call once an argument to a curried function is initialized, that function instance will have that argument fixed, no mater what other instances you create later.
You can also call the curried function as shown in the following snippet. It works identically to fooBar(arg1)(arg2)(arg3)
.
let callableOne = fooBarCurriedVersion(arg1);
let callableTwo = callableOne(arg2);
let result = callableTwo(arg3);
As you can see, when we are using function currying, the function takes a single argument and returns a callable function, which takes the next argument and returns another callable function, and it does it until all arguments are exhausted.
In fact, you can also call it as shown in the following snippet.
let callable = fooBarCurriedVersion(1);
let result = callable(2)(3);
So that’s the basics of function currying in JavaScript. In the next section, we’ll go through a real-world example to demonstrate how it works.
A Real-World Example
Now, you know how function currying works in JavaScript. In this section, we’ll see how you can use it in your day-to-day JavaScript development.
First of all, let’s look at the following function, which calculates the final price of a product after adding necessary charges and applying a discount.
function calFinalPrice(actualPrice, charges, discountRate) {
var finalPrice;
finalPrice = actualPrice + charges - (actualPrice * discountRate/100);
}
Now, you could call this function as shown in the following snippet.
const actualPrice = 100;
const charges = 20;
// get discount rate from the configuration
const discountRate = getDiscountRateFromConfig();
calFinalPrice(actualPrice, charges, discountRate);
As you can see, we’re fetching the discount rate from the configuration, so it’s going to be fixed every time. So we could avoid passing it in the third argument every time we call the calFinalPrice
function, if we create a curried version of the function as shown in the following snippet.
Firstly, we’ve implemented the calFinalPriceWithDiscount
function, which is the curried version of the calFinalPrice
function. In the first argument of the calFinalPriceWithDiscount
function, we’ve passed the discount rate which will be used later on to calculate the final price of the product.
The discVersionFunc
variable holds the callable, which takes two arguments, actual price and charges. Since the discVersionFunc
version wraps the discount rate in a closure, you don’t need to pass it every time you need to calculate the final price of the product.
Finally, you can calculate the final price of a product by passing the price and charges values as shown in the above snippet.
Now, let’s say you want to provide a special discount for some duration, and you can still use the calFinalPriceWithDiscount
function as shown in the following snippet.
const speicalDiscountRate = getSpecialDiscountRateFromConfig();
const specialDiscVersionFunc = calFinalPriceWithDiscount(speicalDiscountRate);
alert(specialDiscVersionFunc(100)(25));
alert(specialDiscVersionFunc(200)(15));
As you can see, the main benefit of function currying is that when you need to call a function with the same parameters repeatedly, you can reuse and refactor your code which becomes much easier to maintain over the period of time.
When to Use Function Currying?
Function currying is a technique that can be helpful in some cases, but it's not something you want to do with all your functions by default. Consider function currying when it helps you with one of these goals:
- writing cleaner code
- removing expensive computations
- creating a single-argument function for
map
orforEach
Write Cleaner Code With Less Repetition
Sometimes function currying can simplify your code. Suppose you have a logging function logToFile(filename, appname, text)
that logs some text to a file. To make it easier to add logging statements in your code, you might like to set the filename and app name once and then simply write something like log(text)
. To achieve that, you can create a curried version of the logging function:
//a curryed version of the logging function
const logCurried = filename => appname => text => logToFile(filename, appname, text)
//create logging functions for a specific file and app
let log = logCurried("somepath/filename-error.log")("My App")
let logWarning = logCurried("somepath/filename-warning.log")("My App")
//now we can use the functions like this:
log("an error occurred")
logWarning("just a warning")
Remove Expensive Computations
Another use for curried functions is to save expensive computations—like file I/O or database reads. For example, say you have the following function:
//get attributes from an item in the database
function getItemAttribute (id, attribute) {
//read the item from the database
let item = databaseRead(id)
//return the requested attribute value
return item[attribute]
}
//get some attributes from a particular item
let color=getItemAttribute("item001", "color")
let shape=getItemAttribute("item001", "shape")
let size=getItemAttribute("item001", "size")
Suppose we wanted to read a number of attributes from the same function in a row. This would mean reading the same database record a number of times, which is wasteful and slow. We could re-write the getItemAttribute
function with currying to make this more efficient:
//get attributes from an item in the database
function getItemAttribute (id) {
//read the item from the database
let item = databaseRead(id)
//and return a function to get attributes from that item
return attribute => item[attribute]
}
//get some attributes from a particular item
const item001 = getItemAttributes("item001")
let color=item001("color")
let shape=item001("shape")
let size=item001("size")
Create a Single-Argument Function for map
or forEach
Using the map
and forEach
methods, you can apply a function to each element of an array. However, these methods expect the function to have a certain signature—usually you'll just want to use a method with a single parameter. Using function currying, you can create a version of any function that can be used with map
or forEach
.
//curried version of Math.pow()
const pow = x => y => Math.pow(x,y)
//now we can use pow in map
const nums=[1,2,3,4,5,6]
const squares=nums.map(pow(2)))
// squares=[1,4,9,16,25,36]
const cubes=nums.map(pow(3)))
// cubes=[1,8,27,64,125,216]
You can learn more about map
and forEach
in one of our other JavaScript tutorials.
- JavaScriptHow to Use Map, Filter, and Reduce in JavaScript
- JavaScriptJavaScript map vs. forEach: When to Use Each One
Conclusion
Today, we discussed how function currying works in JavaScript. We also went through a couple of real-world examples to understand the practical applications of function currying in JavaScript.
Original Link: https://code.tutsplus.com/tutorials/understanding-function-currying-in-javascript-and-when-to-use-it--cms-37867
TutsPlus - Code
Tuts+ is a site aimed at web developers and designers offering tutorials and articles on technologies, skills and techniques to improve how you design and build websites.More About this Source Visit TutsPlus - Code