JavaScript Interview Prep
Functions Deep Dive

Function Currying

The Assembly Line

LinkedIn Hook

"Implement a generic curry function that turns f(a, b, c) into f(a)(b)(c)."

This is the interview question that separates candidates who've played with functional programming from those who've only read about it.

Currying is the transformation that makes partial application natural. Instead of writing one function that takes every argument at once, you chain unary functions — each one locking in a piece of config and returning the next link in the chain. request("POST")("https://api.example.com")("/users")(data).

Suddenly you have specialized functions for free. const post = request("POST") becomes a reusable builder. const handleNavClick = handleEvent("Navigation") becomes a focused event factory.

In this lesson you'll see manual currying, partial application, the difference between the two, and a production-quality generic curry utility that supports both curry(2)(3)(4) and curry(2, 3)(4).

Read the full lesson -> [link]

#JavaScript #InterviewPrep #Currying #PartialApplication #FunctionalProgramming #Closures #CodingInterview


Function Currying thumbnail


What You'll Learn

  • The definition of currying and how it transforms f(a, b, c) into f(a)(b)(c)
  • The difference between currying and partial application
  • How to implement a generic curry utility that supports flexible invocation

One Argument at a Time

Currying is transforming a function that takes multiple arguments — f(a, b, c) — into a sequence of functions that each take one argument: f(a)(b)(c).

Think of it like an assembly line. Instead of one worker assembling the entire product at once, each station adds one piece. The first station adds the base, passes it to the next station which adds the engine, which passes it to the next which adds the paint. Each step is a function that takes one argument and returns the next step.

Basic Currying

// Normal function
function addNormal(a, b, c) {
  return a + b + c;
}
console.log(addNormal(1, 2, 3)); // 6

// Curried version
function addCurried(a) {
  return function(b) {
    return function(c) {
      return a + b + c;
    };
  };
}

console.log(addCurried(1)(2)(3)); // 6

// Arrow function version (same thing, more concise)
const addCurriedArrow = a => b => c => a + b + c;
console.log(addCurriedArrow(1)(2)(3)); // 6

Partial Application with Currying

// Create specialized functions by partially applying arguments
const addCurried = a => b => c => a + b + c;

const add10 = addCurried(10);      // partially applied — "a" is locked to 10
const add10and20 = add10(20);      // partially applied — "b" is locked to 20

console.log(add10(20)(30));  // 60
console.log(add10and20(30)); // 60
console.log(add10and20(50)); // 80

// Practical: API request builder
const request = method => baseUrl => endpoint => data => {
  console.log(`${method} ${baseUrl}${endpoint}`, data);
  // In real code: return fetch(...)
};

const get = request("GET");
const post = request("POST");

const getFromAPI = get("https://api.example.com");
const postToAPI = post("https://api.example.com");

getFromAPI("/users")({});        // "GET https://api.example.com/users {}"
postToAPI("/users")({ name: "Rakibul" }); // "POST https://api.example.com/users {name: 'Rakibul'}"

Practical Use: Event Handler Factories

// Without currying — repetitive
function handleClick(section, action, event) {
  console.log(`[${section}] ${action}`, event.target);
}

// With currying — reusable factories
const handleEvent = section => action => event => {
  console.log(`[${section}] ${action}`, event.target);
};

const handleNavClick = handleEvent("Navigation");
const handleNavOpen = handleNavClick("open");
const handleNavClose = handleNavClick("close");

// In React JSX:
// <button onClick={handleNavOpen}>Open</button>
// <button onClick={handleNavClose}>Close</button>

Implement a Generic Curry Utility

function curry(fn) {
  return function curried(...args) {
    // If we have enough arguments, call the original function
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    }
    // Otherwise, return a function that collects more arguments
    return function(...moreArgs) {
      return curried.apply(this, args.concat(moreArgs));
    };
  };
}

// Usage
function multiply(a, b, c) {
  return a * b * c;
}

const curriedMultiply = curry(multiply);

// All of these produce 24:
console.log(curriedMultiply(2)(3)(4));    // 24
console.log(curriedMultiply(2, 3)(4));    // 24
console.log(curriedMultiply(2)(3, 4));    // 24
console.log(curriedMultiply(2, 3, 4));    // 24

// Partial application works naturally
const double = curriedMultiply(2)(1);
console.log(double(5));  // 10
console.log(double(10)); // 20

Currying vs Partial Application

// CURRYING: f(a, b, c) -> f(a)(b)(c) — always unary (one arg at a time)
const curriedAdd = a => b => c => a + b + c;

// PARTIAL APPLICATION: fix some arguments, return function for the rest
function partial(fn, ...fixedArgs) {
  return function(...remainingArgs) {
    return fn(...fixedArgs, ...remainingArgs);
  };
}

function add(a, b, c) {
  return a + b + c;
}

const add10 = partial(add, 10);
console.log(add10(20, 30)); // 60

const add10and20 = partial(add, 10, 20);
console.log(add10and20(30)); // 60

Function Currying visual 1


Common Mistakes

  • Confusing currying with partial application — currying always produces a chain of unary (single-argument) functions. Partial application just fixes some arguments up front and returns a function that takes the rest all at once.
  • Forgetting fn.length in a generic curry utility — that's how the curried wrapper knows when it has enough arguments to actually invoke the original. Without it, the chain never terminates.
  • Over-currying everything — currying shines for builder patterns, event factories, and config APIs. For a simple add(2, 3) it just makes code harder to read. Use it where it buys real reuse.

Interview Questions

Q: What is currying?

Currying transforms a function that takes multiple arguments into a sequence of functions, each taking a single argument. f(a, b, c) becomes f(a)(b)(c).

Q: What is the difference between currying and partial application?

Currying always transforms a function into a chain of unary (single-argument) functions. Partial application fixes some arguments of a function and returns a new function that takes the remaining arguments — but that returned function can take multiple arguments at once.

Q: Implement a generic curry function.

See the curry utility above. Key: check if accumulated args length >= original function's .length. If yes, call the function. If no, return a new function that collects more arguments.

Q: Give a practical use case for currying.

Configuration functions (API builders where you lock in the base URL and method), event handler factories (lock in section/action, receive event), logging utilities (lock in the log level), and validation functions (lock in the validation rule, apply to different values).

Q: Transform f(a, b) into its curried form.

const curriedF = a => b => f(a, b);. Now curriedF(x)(y) equals f(x, y), and curriedF(x) gives you a reusable function that already knows a.


Quick Reference — Cheat Sheet

CURRYING — QUICK MAP

Definition:
  f(a, b, c)  ->  f(a)(b)(c)
  Multi-arg fn  ->  chain of unary fns.

Hand-written:
  const add = a => b => c => a + b + c;
  add(1)(2)(3)   // 6

Generic utility:
  const curry = fn => function c(...a) {
    return a.length >= fn.length
      ? fn(...a)
      : (...b) => c(...a, ...b);
  };

Curry vs Partial application:
  Curry    -> always unary, full chain
  Partial  -> fix N args up front, rest can be multi-arg

Use cases:
  - API/request builders     request(method)(url)(path)(body)
  - Event handler factories  handle(section)(action)(event)
  - Configured loggers       log(level)(message)
  - Reusable validators      validate(rule)(value)

Previous: Pure Functions & Side Effects Next: Function Composition -> The Pipeline


This is Lesson 6.5 of the JavaScript Interview Prep Course — 14 chapters, 87 lessons.

On this page