Currying in Functional Programming
Currying is a functional programming technique where a function is transformed to take arguments one at a time. Instead of taking all arguments at once, a curried function returns another function that takes the next argument, and so on, until all arguments are provided. This technique enables more reusable, flexible, and composable code.
What is Currying?
Currying converts a function with multiple arguments into a series of functions, each taking a single argument.
Standard Function:
function add(a, b) {
return a + b;
}
console.log(add(2, 3)); // 5
Curried Version:
function curriedAdd(a) {
return function (b) {
return a + b;
};
}
const addTwo = curriedAdd(2);
console.log(addTwo(3)); // 5
console.log(curriedAdd(2)(3)); // 5
Currying Benefits
- 1Function Reusability: Preconfigure arguments to create specialized functions.
- 2Function Composition: Simplifies the chaining of functions.
- 3Improved Readability: Logical flow becomes more explicit.
Manual Currying with Closures
Closures play a vital role in implementing currying. Let’s create a manual example of a curried function.
function multiply(a) {
return function (b) {
return function (c) {
return a * b * c;
};
};
}
console.log(multiply(2)(3)(4)); // 24
Here, each level of the function remembers its input and passes it along to the next level.
Using a Helper Function to Curry
Instead of manually currying, we can create a generic currying utility.
function curry(func) {
return function curried(...args) {
if (args.length >= func.length) {
return func(...args);
} else {
return (...nextArgs) => curried(...args, ...nextArgs);
}
};
}
function sum(a, b, c) {
return a + b + c;
}
const curriedSum = curry(sum);
console.log(curriedSum(1)(2)(3)); // 6
console.log(curriedSum(1, 2)(3)); // 6
console.log(curriedSum(1)(2, 3)); // 6
Practical Use Case: Filtering Data
Suppose you need a reusable function to filter a dataset.
const filterBy = curry((key, value, array) => {
return array.filter(item => item[key] === value);
});
const filterByCategory = filterBy("category");
const products = [
{ name: "Laptop", category: "Electronics" },
{ name: "Shirt", category: "Clothing" },
{ name: "Phone", category: "Electronics" },
];
console.log(filterByCategory("Electronics")(products));
// [{ name: "Laptop", category: "Electronics" }, { name: "Phone", category: "Electronics" }]
Built-in Support: Arrow Functions and Currying
JavaScript arrow functions make curried functions concise.
const multiply = a => b => c => a * b * c;
console.log(multiply(2)(3)(4)); // 24
When to Use Currying?
- 1Preconfigured Functions: Create functions with some arguments pre-filled.
- 2Functional Composition: Chain operations easily by partially applying functions.
- 3Simplified Testing: Test parts of the function independently.
Common Pitfalls and Best Practices
- 1Over-currying: Don’t curry unnecessarily, as it might make the code harder to understand.
- 2Flexible Implementation: Use a currying utility for larger projects.
Conclusion
- Currying transforms a function into smaller, single-argument functions.
- It improves reusability and composition in functional programming.
- JavaScript supports currying through closures and utility functions.