Have you seen the word "currying" in JavaScript and wondered what it meant? In this blog, we'll look at the concept of currying, break it down with easy examples, and show how it can be used in a real-world scenario to make your code clearer and more flexible.
š” What is Currying?
Currying is a functional programming approach in which a function uses each of its arguments one at a time, rather than all at once. A curried function returns another function that accepts the next parameter until all arguments have been provided.
In simple terms, currying converts a function with several arguments into a series of functions, each of which takes one argument.
Let's understand using a real-life analogy and code:
š Make a Burger
Think about ordering a burger at a fast-food eatery. A chef will prepare your burger layer by layer:
Layer 1: The bun (first argument).
Layer 2: The patty (second argument).
Layer 3: Toppings (third argument).
Let's write code for the above scenario using Regular Function and Curried Function.
š Using a Regular Function:
In a regular function, all the ingredients are passed at once as arguments.
function makeBurger(bun, patty, topping) {
return `Your burger has: ${bun} bun, ${patty} patty, and ${topping} topping.`;
}
const myBurger = makeBurger("Sesame", "Mix Veg", "Cheese");
console.log(myBurger); // Output: Your burger has: Sesame bun, Mix Veg patty, and Cheese topping.
š Using a Curried Function:
In a curried function, you pass one ingredient at a time.
function makeBurgerCurried(bun) {
return function (patty) {
return function (topping) {
return `Your burger has: ${bun} bun, ${patty} patty, and ${topping} topping.`;
};
};
}
// Example usage
const chooseBun = makeBurgerCurried("Sesame");
const choosePatty = chooseBun("Mix Veg");
const myCurriedBurger = choosePatty("Cheese");
console.log(myCurriedBurger); // Output: Your burger has: Sesame bun, Mix Veg patty, and Cheese topping.
āļø Explanation:
First Call: makeBurgerCurried("Sesame")
receives "Sesame"
and returns a new function that waits for patty.
const chooseBun = makeBurgerCurried("Sesame");
console.log(chooseBun);
/* Output:
Ę (patty) {
return function (topping) {
return `Your burger has: ${bun} bun, ${patty} patty, and ${topping} topping.`;
};
} */
Second Call: chooseBun("Mix Veg")
receives "Mix Veg"
and returns another function that waits for topping.
const choosePatty = chooseBun("Mix Veg");
console.log(choosePatty);
/* Output:
Ę (topping) {
return `Your burger has: ${bun} bun, ${patty} patty, and ${topping} topping.`;
} */
Third Call: choosePatty("Cheese")
receives "Cheese"
and completes the function chain, returning the final burger description.
const myCurriedBurger = choosePatty("Cheese");
console.log(myCurriedBurger);
// Output: Your burger has: Sesame bun, Mix Veg patty, and Cheese topping.
ā Simplified Arrow Function for Currying
You can simplify the curried function using arrow functions:
const curriedArrowFunction = (bun) => (patty) => (topping) =>
`Your burger has: ${bun} bun, ${patty} patty, and ${topping} topping`
const myArrowFunction = curriedArrowFunction("Sesame")("Mix Veg")("Cheese")
console.log(myArrowFunction); // Your burger has: Sesame bun, Mix Veg patty, and Cheese topping
āļø Why Use Currying?
Currying is especially handy when you need to reuse a function with specific arguments. It promotes code reuse, readability, and modularity.
š» Real-World Application: Discount Calculator
Imagine you're developing an e-commerce platform. Discounts are calculated according to the type of customer:
- Regular customers receive a 10% discount.
- Premium customers receive a 20% discount.
Now let's build this using the Regular function first:
š Using a Regular Function:
Using a regular function for a discount calculator may result in less flexibility and reusable code. You'd have to write separate functions for each type of customer or pass all parameters every time the discount is calculated.
function calculateDiscount(customerType, price) {
if (customerType === "Regular") {
return price * 0.9; // 10% discount
} else if (customerType === "Premium") {
return price * 0.8; // 20% discount
}
}
console.log(calculateDiscount("Regular", 100)); // Output: 90
console.log(calculateDiscount("Premium", 100)); // Output: 80
ā Limitations of Regular Function:
- Repetitive Logic: You must pass customerType every time, even if it does not change during several calculations.
- No Reusability: If you want to apply a discount to one customer type across multiple transactions, you must specify the type each time.
- Scalability Issues: Adding additional customer types or discount rules complicates the function and makes it more difficult to maintain.
Now let's build this application using the Curried function:
š Using a Curried Function:
Currying allows you to create reusable functions for various customer types. Instead of continuously giving the same parameters, you can configure the discount logic for each consumer type.
function createDiscountCalculator(discountRate) {
return function (price) {
return price * (1 - discountRate);
};
}
// Create specific calculators for different customer types
const regularDiscount = createDiscountCalculator(0.1); // 10% discount
const premiumDiscount = createDiscountCalculator(0.2); // 20% discount
// Use them for calculations
console.log(regularDiscount(100)); // Output: 90
console.log(premiumDiscount(100)); // Output: 80
console.log(regularDiscount(200)); // Output: 180
ā Advantages of the Curried Function:
-
Reusability: Once
regularDiscount
orpremiumDiscount
is specified, you will not need to specify the discount rate again for further transactions. - Cleaner Code: The logic is separate and focused. Each function has a single responsibility: to define and apply the discount rate.
- Scalability: Creating new customer types is simple. For example:
const studentDiscount = createDiscountCalculator(0.15); // 15% discount
console.log(studentDiscount(100)); // 85
-
Improved Readability: The code clearly states its purpose. A
regularDiscount
function specifies the discount logic for regular customers.
Conclusion
Currying may appear complex at first, but as we've seen, it's a powerful concept for simplifying function creation and making your code clearer and more reusable.
Now that you get an idea of it, try currying in your next project and see the magic happen!
Share Your Thoughts
Have you ever used currying in your projects? What were the problems or benefits you experienced? Please let me know in the comments below!
Happy coding!āØ
Top comments (4)
You are totally right a curryng is a great tool of JS programming.
Great to put into the table!
But the standard function is a different question.
A way more elegant declaration of currying is a arrow function.
I am using this technic in my program, there you can found the repo link in this post: dev.to/pengeszikra/javascript-grea...
Thanks for the comment!
You are correct, arrow functions are an easier way to write curried functions. They provide clarity and context, making them useful for this purpose.
I appreciate that you included a practical application of currying in your program. It's always interesting to see how theoretical concepts apply to practical implementations. I'll surely check that.
Currying is usually seen as one of those advanced concepts. Breaking a function into smaller parts definitely improves reusability and clarity. I like it because it eliminates the need to repeatedly pass the same arguments and this makes your code more flexible and more maintainable. Good job!
Thanks for the comment!
Yeah, currying improves reusability and clarity by dividing down functions. It eliminates unnecessary parameters, increasing code flexibility and maintainability.
I'm glad you find it useful.