Explain currying and partial application in JavaScript

Advanced18 min interview
Skills tested:
Currying: transforming N-ary functions into chains of unary functionsPartial application using bind and closuresWriting a generic curry utility and understanding fn.length limitationsComposing specialized functions from general onesIdentifying when currying breaks for variadic functions

Advertisement

🧩 Scenario

In a real codebase, partial application appears whenever you have a generic function that takes configuration arguments followed by data arguments. API request builders, date formatters, validators, and logging utilities all benefit from pre-filling the configuration and passing around a specialized function. Currying is common in functional programming pipelines and libraries like Ramda. Understanding both patterns lets you write composable, DRY utilities and read library code that relies on them.

Architecture Walkthrough

Currying: Chains of Unary Functions

Currying transforms a function that takes N arguments into a chain of N functions that each take one argument. Calling a curried function with one argument does not produce a result; it produces a new function waiting for the next argument. Only when all required arguments are supplied does the original function execute and return the final value.

The core use case is creating specialized variants of a general function by fixing arguments progressively. Each intermediate function in the chain is a fully reusable, composable unit. This makes curried functions easy to combine in pipelines where each step applies one transformation.

Partial Application: Pre-Filling Arguments

Partial application is different from currying: it pre-fills one or more arguments of a function and returns a new function that accepts the remaining arguments all at once. The resulting function does not have to be called one argument at a time. JavaScript's built-in bind is a partial application mechanism: fn.bind(context, arg1, arg2) returns a new function where arg1 and arg2 are already fixed.

Partial application is generally more pragmatic than full currying for typical JavaScript code. You rarely need a chain of six single-argument calls. More often, you want to fix a configuration argument (locale, base URL, log level) and pass around a function that accepts the remaining data argument.

The fn.length Limitation and Arity

A generic curry utility uses fn.length to determine when enough arguments have been collected to invoke the original function. fn.length returns the number of parameters declared in the function signature, not including rest parameters or parameters with defaults. A variadic function written as function sum(...nums) has fn.length === 0, so a naive curry utility cannot automatically determine when to invoke it.

The workaround is to pass the expected arity explicitly to the curry utility: curry(fn, 3). Libraries like Ramda handle this by requiring you to specify arity separately for variadic functions.


Key Code Explained

// Manual curry: two-argument example
function multiply(a) {
  return function (b) {
    return a * b;
  };
}
const double = multiply(2); // b => 2 * b
const triple = multiply(3);
double(5); // 10
triple(5); // 15

// Generic curry utility
function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    }
    return function (...moreArgs) {
      return curried.apply(this, args.concat(moreArgs));
    };
  };
}

const add = curry((a, b, c) => a + b + c);
add(1)(2)(3); // 6 — one arg at a time
add(1, 2)(3); // 6 — batching works too
add(1)(2, 3); // 6 — any split

// Partial application with bind
function formatDate(locale, timezone, date) {
  return new Intl.DateTimeFormat(locale, { timeZone: timezone }).format(date);
}
const formatIST = formatDate.bind(null, 'en-IN', 'Asia/Kolkata');
formatIST(new Date()); // formatted in IST, only the date changes per call

// Practical API client example using curry
const request = curry(async (method, baseUrl, endpoint, body) => {
  const res = await fetch(`${baseUrl}${endpoint}`, {
    method,
    body: body ? JSON.stringify(body) : undefined,
    headers: { 'Content-Type': 'application/json' },
  });
  return res.json();
});

const getFromApi = request('GET')('https://api.example.com');
const postToApi = request('POST')('https://api.example.com');

// Only the endpoint (and body for POST) changes per call
const user = await getFromApi('/users/42');
await postToApi('/submissions', { challengeId: 'io-001' });

The API client example is the most instructive real-world use. getFromApi and postToApi are specialized functions derived from one general request function. The method and base URL are fixed once; callers only provide what varies per request.


Tradeoffs

ApproachProCon
Manual curryingFull control, no utility dependencyVerbose for functions with many arguments
Generic curry utilityDRY, flexible calling styleBreaks for variadic functions, adds indirection
Partial application with bindNative, zero dependenciesPositional, only fills leading arguments
Closure-based partial applicationFlexible, named parametersMore boilerplate than bind for simple cases

What Interviewers Actually Check

  • Whether you can clearly distinguish currying from partial application
  • Whether you can write a generic curry utility and explain how it uses fn.length
  • Whether you know fn.length is 0 for variadic functions and can describe the consequence
  • Whether you can name a real use case where either pattern eliminates code duplication
  • Whether you understand that bind is a built-in partial application tool

Follow-Up Questions

  1. How would you implement compose or pipe utilities that work well with curried functions?
  2. How does Ramda's approach to currying differ from a simple recursive curry utility?
  3. If a curried function is called with too many arguments all at once, what does a well-implemented curry utility do?
  4. How does TypeScript handle the return type inference of a generic curry utility?
  5. A teammate uses _.curry from Lodash on every utility function by default. What questions would you ask before merging the PR?

Common Candidate Mistakes

  • Using "currying" and "partial application" as synonyms when they describe different transformation patterns
  • Writing a curry utility and not knowing it fails for functions that use rest parameters because fn.length returns 0
  • Thinking curried functions must be called one argument at a time when a proper implementation supports batching
  • Not knowing that Function.prototype.bind is a built-in partial application mechanism
  • Applying currying to functions that are always called with all arguments, adding complexity with no benefit

Interview Readiness Checklist

Before you leave this question, make sure you can answer:

  • Can you explain the difference between currying and partial application without confusing them?
  • Can you write a manually curried two-argument function and a generic curry utility?
  • Can you explain why fn.length breaks for variadic functions and how to work around it?
  • Can you use Function.prototype.bind for partial application with a concrete example?
  • Can you name a real use case where currying or partial application reduces code duplication?

Summary

Currying transforms a function of N arguments into a chain of N unary functions. Each call returns a new function that waits for the next argument until all have been provided. Partial application is a looser pattern: you pre-fill one or more arguments and return a function that accepts the remainder, all at once.

Both patterns produce specialized functions from general ones, reducing repetition when the same argument values are used across many call sites. Function.prototype.bind provides partial application natively. Generic curry utilities can handle any function but break when rest parameters are used because fn.length reports 0 for variadic signatures.

The practical value of these patterns is most visible in API clients, formatters, and validators, where configuration arguments are fixed at setup time and data arguments vary per call. For most everyday JavaScript, partial application with closures or bind is sufficient; full currying is more valuable in functional programming pipelines where point-free composition is the goal.

Related Videos
Watch these videos to learn more about this topic
Frequently Asked Questions

Is currying the same as partial application?

Not exactly. Currying transforms a function of N args into N functions of 1 arg each. Partial application pre-fills some arguments and returns a function that accepts the rest.

Advertisement


Stay Updated

Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.

Advertisement