Do you love curry in your food? I have to be honest; I don’t. But I’m sure some of my readers can’t live without it :D.
Ever glanced at a kitchen and wondered, “How do spices relate to coding?”
Today, let’s talk about the magic of JavaScript ‘Currying’, and by the end, you’ll not only appreciate this flavorful technique but also savor its applications!
A Quick Curry Dish
When you hear “curry”, you might think of a tantalizing Indian dish. But in JavaScript, currying refers to a technique of transforming a function that takes multiple arguments into a series of functions that each take a single argument.
Confused? Let’s check a simple example:
// Traditional Function
function add(x, y) {
return x + y;
}
console.log(add(2, 3)); // Outputs: 5
This is a simple function that adds two numbers. But how about we transform it with some currying magic?
// Curried Function
function add(x) {
return function(y) {
return x + y;
};
}
console.log(add(2)(3)); // Outputs: 5
In the curried version, add(2)
returns a function that expects another argument y
. Only when the second function receives its argument do we get the result.
Why Curry?
You might be asking yourself, why would anyone take a straightforward function and, seemingly, make it more complicated?
- Flexibility: Currying allows for partial application of functions. This means you can fix some number of arguments, creating a new function with a reduced number of arguments.
- Readability: With currying, you can produce more readable and modular code. It allows you to build layers of functions, making each one handle its own logic.
- Function Composition: Currying plays nicely with higher-order functions and function composition, enabling smoother data flow and chaining.
A Tasteful Example
Let’s understand the flexibility currying offers with another example.
Suppose you’re building a tax calculation system. You want to create a generic function to apply various tax rates to different product prices:
function tax(rate) {
return function(amount) {
return amount + (amount * rate);
};
}
const applyVAT = tax(0.2);
const applyGST = tax(0.05);
console.log(applyVAT(100)); // Outputs: 120 (20% tax on 100)
console.log(applyGST(100)); // Outputs: 105 (5% tax on 100)
Here, we curried our tax function. applyVAT
and applyGST
are now specialized versions of our generic tax function. Instead of redefining functions for each tax rate, we just curry and go!
Function Composition and Currying
Combining currying with function composition can lead to elegant and functional code patterns:
// This is a very common compose function, nothing fancy
function compose(...funcs) {
return funcs.reduce((f, g) => (...args) => f(g(...args)));
}
const double = x => x * 2;
const increment = x => x + 1;
const transform = compose(increment, double); // Double then increment
console.log(transform(5)); // Outputs: 11 (5 * 2 + 1)
Here, compose
is a higher-order function, taking functions as arguments and returning a function. Currying allows us to chain and combine functions, making our code more DRY and modular.
If you want to know more about composition, I have another article that explains it in more detail:
Understand JavaScript Composition Once and for All
When we embark on the coding journey with JavaScript, we encounter numerous concepts, and Function Composition is one…
blog.stackademic.com
A Real World Example of Javascript Currying
All the theory is beautiful and works well on paper. But when I was first learning about currying, I constantly wondered how I could apply it to real scenarios. I felt like I was in the dark, searching for situations where I could implement currying over standard functions.
This is what I wrote in 2017 (6 years ago) when I finally began to grasp the real applications of currying:
How Javascript ‘Currying’ finally has made sense to me
Functional programming has a lot of cool concepts and techniques. I was amazed by each one of them when I started…
medium.com
Now, 6 years later, I come armed with even more real-world examples. Dive into this case that might further illuminate your understanding.
Let’s consider an example where you want to filter, transform, and then summarize data from an array of products. Each operation is based on different criteria or functions, and currying, combined with function composition, can make this process more elegant.
Imagine you have an e-commerce site with various products, and you want to:
- Filter out products with prices below a certain threshold.
- Apply a discount to the remaining products.
- Calculate the total price after discounts.
Here’s how we can use currying and composition to achieve this:
// Curried functions
const filterByPrice = threshold => products => products.filter(p => p.price >= threshold);
const applyDiscount = percentage => products => products.map(p => ({ ...p, price: p.price * (1 - percentage) }));
const calculateTotal = products => products.reduce((acc, p) => acc + p.price, 0);
// Function composition helper
const compose = (...funcs) => data => funcs.reduce((value, func) => func(value), data);
// Sample products
const products = [
{ id: 1, name: 'Laptop', price: 1000 },
{ id: 2, name: 'Mouse', price: 50 },
{ id: 3, name: 'Keyboard', price: 150 },
{ id: 4, name: 'Monitor', price: 300 },
{ id: 5, name: 'Headphones', price: 80 }
];
// Combined function using currying and composition
const getTotalAfterDiscount = compose(
filterByPrice(100), // Filters out products priced below 100
applyDiscount(0.1), // Applies a 10% discount
calculateTotal // Calculates the total price
);
console.log(getTotalAfterDiscount(products)); // Outputs: 1305 (90% of 1000 + 90% of 300 + 90% of 150)
In this example, currying allows us to set specific criteria or operations and then chain them together seamlessly using function composition. The composition allows for better separation of concerns, making the code more readable and modular.
In real-world applications, such an approach is beneficial when you have multiple transformation or filtering operations that might change or vary independently.
A Pinch of Salt
While currying is incredibly powerful, remember, it’s just another tool in your JS toolkit. Overusing it can make the code harder to understand for someone unfamiliar with the concept. Use it where it makes sense and adds value.
Final Thoughts
Currying isn’t about complicating things; it’s about creating modular, reusable, and more functional JavaScript. It’s like preparing ingredients for a meal. By having them prepped and ready, you can create a variety of dishes effortlessly!
So, next time you’re in a coding session, give currying a try. And who knows? Maybe you’ll develop a taste for it!