Suggest an editImprove this articleRefine the answer for “What is currying in JavaScript”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Currying** in JavaScript transforms a function with multiple arguments into a chain of single-argument functions, each returning the next until all args are collected. ```javascript const add = a => b => c => a + b + c; add(1)(2)(3); // 6 const add5 = add(5); // partial application - fix one arg add5(3); // 8 add5(10); // 15 ``` **Key:** each call returns a function, not a value, until the last argument arrives.Shown above the full answer for quick recall.Answer (EN)Image**Currying** is a technique that transforms a function taking multiple arguments into a chain of single-argument functions, where each call returns the next function until all arguments are collected and the original logic runs. ## Theory ### TL;DR - Analogy: like a coffee order step by step - choose size (returns milk options), then milk (returns syrup), instead of saying everything at once - Each curried call returns a new function that closes over the previous argument - Regular: `sum(1, 2, 3)`. Curried: `sum(1)(2)(3)`. Same result, different shape - Use it when you reuse fixed args (base URL, user role, log level). Skip it for one-off calls - Partial application falls out naturally: stop halfway, save the result, reuse it ### Quick example ```javascript // Regular - all args at once function regularSum(a, b, c) { return a + b + c; } regularSum(1, 2, 3); // 6 // Curried - one arg at a time const currySum = a => b => c => a + b + c; currySum(1)(2)(3); // 6 // Partial application: fix one arg, reuse the rest const add5 = currySum(5); add5(3); // 8 add5(10); // 15 ``` Each call returns a function that holds the previous argument in its closure. The final call runs the logic with everything it needs. ### Key difference A regular function takes all its arguments at once and either runs or produces `NaN`/`undefined` if something is missing. A curried function accepts them one at a time, so you can stop halfway, store the partially-applied result, and reuse it across your codebase. That stored intermediate is called a partial application. ### When to use - Reusing a fixed argument across many calls (API base URL, user role in auth middleware) - curry it, create specialized versions once - Building function pipelines where each step takes one input - Config-heavy utilities like loggers where the level is set once and reused - One-shot math or a simple transform with no reuse - plain function is cleaner ### How JavaScript handles currying V8 creates a new closure on each curried call. Each closure captures prior arguments in its lexical scope, no global state. When the final argument arrives, the engine reconstructs the full argument list and calls the original function. Short closure chains get inlined by V8. Longer ones may allocate on the heap, so profile before currying inside tight loops. ### Universal curry utility Nesting functions manually gets old fast. A universal curry wrapper checks how many arguments the function expects (`fn.length`) and keeps collecting until it has enough: ```javascript function curry(fn) { return function curried(...args) { if (args.length >= fn.length) return fn(...args); return (...nextArgs) => curried(...args, ...nextArgs); }; } const max = (a, b) => Math.max(a, b); const curriedMax = curry(max); curriedMax(10)(20); // 20 - one at a time curriedMax(10, 30); // 30 - both at once also works ``` You can pass args one at a time or in groups. The base case fires as soon as the total reaches `fn.length`. ### Common mistakes **Missing `return` in the outer function:** ```javascript // Wrong function bad(a) { function inner(b) { return a + b; } // outer never returns inner } bad(1)(2); // TypeError: bad(...) is not a function ``` The outer must explicitly return the inner. Arrow functions make this harder to miss: `a => b => a + b`. **Assuming JavaScript auto-curries:** ```javascript const add = (a, b) => a + b; const add1 = add(1); // NaN - b becomes undefined, not a new function ``` Native functions don't curry themselves. Wrap with a curry utility or write manually. **Missing the base case in a custom curry:** ```javascript // Wrong - infinite recursion function badCurry(fn) { return function(...args) { return badCurry(fn)(...args); // no exit condition, stack overflow }; } ``` Always check `args.length >= fn.length` before recursing. **Currying a variadic function:** ```javascript const sum = (...args) => args.reduce((a, b) => a + b, 0); const curriedSum = curry(sum); // fn.length is 0, base case fires immediately curriedSum(1, 2, 3); // works, but currying has no effect ``` `fn.length` returns 0 for rest parameters. Pass the expected arity explicitly if you need it. ### Real-world usage In most codebases, currying first shows up not in a functional programming tutorial but in an auth middleware factory someone wrote on day one and never changed. - **Lodash**: `_.curry` wraps any function for composable chains - `curry(map)(double)(data)` - **Ramda**: all functions curried by default, point-free style via `R.pipe` - **Express**: `requireRole('admin')` middleware factories are the most common real-world pattern - **Redux-Observable**: curried operators like `switchMap` for composing epics - **React fetch hooks**: `const getUser = curryFetch('/api/users')` fixes the base URL once ### Follow-up questions **Q:** What is the difference between currying and partial application? **A:** Partial application fixes some arguments and returns a function expecting the rest. Currying always breaks into single-argument steps. Every curried function supports partial application, but you don't need currying to partially apply. **Q:** Implement a curried version of `Array.prototype.map`. **A:** `const curryMap = fn => arr => arr.map(fn)`. Use as `curryMap(x => x * 2)([1, 2, 3])`, which returns `[2, 4, 6]`. **Q:** How does currying enable function composition? **A:** Curried functions take one input and return one output, which is exactly what `compose` and `pipe` expect. You can build `pipe(trim, toLower, greet)` without wrapper functions around each step. **Q:** What happens when you curry a function with default parameters? **A:** Default parameters don't count in `fn.length`. `function add(a, b = 0)` has `fn.length === 1`. The universal curry fires after just one argument, treating `b` as always defaulting to 0. **Q:** How does V8 optimize curried closures at the engine level? (Senior) **A:** V8 escape analysis avoids heap allocation for short-lived closures. Small curried chains get inlined. Hot paths generating many closures may deoptimize - check with `--trace-ic`. For performance-critical code, `.bind()` can be faster than manual currying because it is a native operation. ## Examples ### Basic: reusable adder ```javascript const add = a => b => a + b; const add10 = add(10); // fix 10, get a reusable adder add10(5); // 15 add10(20); // 30 add10(-3); // 7 ``` `add10` is a plain function that adds 10. Create it once, use it anywhere, without touching the original `add`. ### Intermediate: Express role-based middleware ```javascript const requireRole = role => (req, res, next) => { if (req.user?.role === role) return next(); res.status(403).send('Access denied'); }; const adminOnly = requireRole('admin'); const editorOnly = requireRole('editor'); app.get('/admin/dashboard', adminOnly, dashboardHandler); app.get('/blog/edit', editorOnly, editHandler); // req.user.role === 'admin' -> calls next() // anything else -> 403 ``` Without currying, you'd pass the role at every route definition or duplicate the check. This way the logic lives once and you name the specialized versions. ### Advanced: universal curry with a logger ```javascript function curry(fn) { return function curried(...args) { if (args.length >= fn.length) return fn(...args); return (...nextArgs) => curried(...args, ...nextArgs); }; } function formatMessage(level, timestamp, message) { return `[${level}] ${timestamp}: ${message}`; } const log = curry(formatMessage); const logError = log('ERROR'); // fix level const logErrorNow = logError(new Date().toISOString()); // fix timestamp logErrorNow('Database connection failed'); // [ERROR] 2024-01-15T10:30:00.000Z: Database connection failed logErrorNow('Timeout on /api/users'); // [ERROR] 2024-01-15T10:30:00.000Z: Timeout on /api/users log('INFO', new Date().toISOString(), 'Server started'); // all at once also works ``` The base case (`args.length >= fn.length`) is what makes passing args in groups work alongside one-at-a-time calls.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.