JavaScript
is filled with cool features, and one of the most mind-blowing concepts is currying. Donโt worry if it sounds fancyโby the end of this blog, youโll not only understand currying but also be able to impress your developer buddies! ๐ป๐ฅ
๐ง What is Currying?
Currying is a way to transform a function with multiple arguments into a sequence of functions, each taking a single argument. Itโs like serving a meal one dish at a time instead of all at once! ๐ฝ๏ธ
Read this once again !!
๐ฐ Why Use Currying?
- Reusability: You can create specialized versions of functions.
- Readability: Makes your code look cleaner and more modular.
- Functional Programming Vibes: Functional programming fans ๐ currying.
๐ Examples
Letโs jump straight into some examples:
Basic Example
// Normal 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;
};
}
console.log(curriedAdd(2)(3)); // 5
๐ Boom! Now you can call curriedAdd(2)
and get a reusable function to add 2 to anything!
const add2 = curriedAdd(2);
console.log(add2(5)); // 7
console.log(add2(10)); // 12
Currying with Arrow Functions ๐น
Who doesnโt love short, clean arrow functions?
const multiply = (a) => (b) => a * b;
console.log(multiply(3)(4)); // 12
// Make a multiplier of 3
const triple = multiply(3);
console.log(triple(5)); // 15
console.log(triple(10)); // 30
Real-World Example ๐
Imagine a filter function for a shopping app:
const filterByCategory = (category) => (product) => product.category === category;
const products = [
{ name: "Shoes", category: "Fashion" },
{ name: "Laptop", category: "Electronics" },
{ name: "T-shirt", category: "Fashion" },
];
const isFashion = filterByCategory("Fashion");
console.log(products.filter(isFashion));
// Output: [ { name: "Shoes", category: "Fashion" }, { name: "T-shirt", category: "Fashion" } ]
Breaking Down Complex Problems ๐งฉ
Currying makes it easy to break problems into smaller, manageable parts.
const greet = (greeting) => (name) => `${greeting}, ${name}!`;
const sayHello = greet("Hello");
console.log(sayHello("Alice")); // Hello, Alice!
console.log(sayHello("Bob")); // Hello, Bob!
const sayGoodMorning = greet("Good Morning");
console.log(sayGoodMorning("Charlie")); // Good Morning, Charlie!
๐ Advanced Currying with Utility Functions
Donโt want to manually curry functions? Letโs write a helper function:
const curry = (fn) => (...args) =>
args.length >= fn.length
? fn(...args)
: curry(fn.bind(null, ...args));
// Example:
const sum = (a, b, c) => 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
๐ค Tricky Question Time!
Here's a fun one to tease your brain ๐ง :
const add = (a) => (b) => b ? add(a + b) : a;
console.log(add(1)(2)(3)(4)()); // โ
What do you think the output is? Share your thoughts below! ๐
๐ Wrapping Up
Currying is a powerful technique that simplifies your code, makes it reusable, and adds a touch of elegance. So, start currying your functions and bring the spice of functional programming to your JavaScript code! ๐ถ๏ธ
Happy coding! ๐ปโจ
Top comments (14)
Your trick question goes beyond simple currying to add recursion.
add(1)
returns a function that takes a single argument, call thisaddOne
. LIke this:This returns a closure where
a
is assigned1
.add(1)(2)
is equivalent toaddOne(2)
where2
is theb
argument. Asb
exists and is not0
, this returns the output ofadd(1 + 2)
readadd(3)
. This makes3
thea
in a new closure, so the function it returns could be calledaddThree
.add(1)(2)(3)
then is equivalent toaddThree(3)
which hasa === 3
from the closure returned earlier andb == 3
from the argument. So again we recurse, returningadd(3 + 3)
oraddSix
.So
add(1)(2)(3)(4)
isaddSix(4)
whereb === 4
hence it returnsadd(6 + 4)
which we can calladdTen
.Finally
add(1)(2)(3)(4)()
callsaddTen()
with no arguments, which halts the recursion and just returns thea
value (the sum) from the closure, which is10
.We can see this more clearly like this:
Easy peasy โ if you understand currying, closures, and recursion.
P.S. Your article doesn't really explore why currying is so useful (which is why ALL functions in Haskell are curried). Why would I need an
addThree
function?The most obvious answer is the use of
pipe
orcompose
. Pipe is easier, so here's an example assuming curried arithmetic functions:Note, all functions passed to pipe (except the first one) must be unary, i.e., take only one parameter.
Thanks for sharing your views.
Wow, Nice explanation @chasm
This is well writen and the examples are very easy to follow and illustrate your points well. I have read a few articles about currying from time to time, but it always feels like a bit of mental gymnastics is required to use it. I can follow the examples, but its not something I can look at and instantly say "thats whats going on". Maybe it would become more intuitive if I used it more. Anyway, good write up.
Thanks @davepile
`
`
const add = (a) => (b) => b ? add(a + b) : a;
console.log(add(1)(2)(3)(4)()); // โ
`
`
It's answer would be : 10 ( if I uderstand the concept clearly)
Yes it's correct !!
What's your approach to solve this.
Absolutely incredible! ๐ป.
Thanks @aniruddhadak
It's fantastic.
How does utility function works?? It's confusing
What does this code do
fn(...args)
: curry(fn.bind(null, ...args));
This is actually a pretty smart bit of code. It allows you to curry any JavaScript function (remember that all functions must take at least one parameter, else they are effectively constants).
The function itself is curried, so you can pass in your function (
sum
) and get back a function that takes one or more arguments, which we collect into an array calledargs
:(...args)
.We can check how many arguments
sum
expects withlength
, hencefn.length
here returns3
becausesum(a, b, c)
.So when the curried
sum
is called, we check if the number of arguments passed in (args
) is equal to the number of parameters expected (fn.length
). If all the arguments are there, then we just call thesum
function with them, spreading theargs
back out:fn(...args)
.But if too few arguments have been passed, e.g.,
sum(1, 2)
, then we bind those arguments to their parameters (here,a = 1
andb = 2
) and return a closure that expects the third argument (c
). When it gets that argument, it will meet the conditionfn.length = args.length
, so it will simply call the function with the remaining argument(s).Hope that helps.
I call that a fonctional paradigme.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.