Conceptly
← All Concepts
🧩

Currying

FunctionsTransforming a multi-argument function into a chain of unary functions

Currying is the technique of transforming a function that takes multiple arguments at once into a sequence of functions that each take exactly one argument. Instead of calling f(a, b, c), you call f(a)(b)(c). The payoff is not novelty in the call syntax. The payoff is that each intermediate result is itself a function of one argument, which makes it easy to reuse, compose, and partially apply in a functional pipeline.

Architecture Diagram

🔄 Process

Dashed line animations indicate the flow direction of data or requests

Why do you need it?

Function composition works most cleanly when every stage has the same shape: one input in, one output out. Real application functions often do not look like that. They take configuration, locale, options, thresholds, and data all at once. When you try to insert such a function into pipe, map, or another higher-order utility, the mismatch shows up immediately. You start writing wrapper functions whose only job is to hold onto some arguments while leaving one slot open for the pipeline input. When that pattern repeats, the missing abstraction is currying -- a way to reshape the function itself so it matches the composition model.

Why did this approach emerge?

Currying predates modern functional programming libraries and comes from the mathematical work around lambda calculus and the study of functions as single-argument mappings. Languages such as Haskell and ML made the model explicit: multiple arguments are understood as a chain of unary functions. In JavaScript, currying became more visible through Ramda, Lodash/fp, and other libraries that centered APIs around composition. As functional ideas spread into mainstream application code, currying moved from theory into practice as a tool for producing function shapes that compose well.

How does it work inside?

A curried function processes one argument at a time. The first call stores the first argument and returns a function waiting for the second. That second call stores its argument and returns yet another function waiting for the third. Only when the final argument arrives does the original computation run. Each intermediate function is a closure over the arguments that have already been supplied, so add(1) is effectively 'a function that remembers 1' and add(1)(2) is 'a function that remembers 1 and 2.' Currying does not change what the computation means; it changes the way the computation is staged across calls.

In Code

Turning configuration into a unary function

const addVAT = (rate: number) => (price: number) =>
  price * (1 + rate);

const addKoreanVAT = addVAT(0.1);
addKoreanVAT(10000); // 11000

const prices = [100, 200, 300];
const withVAT = prices.map(addKoreanVAT);
// [110, 220, 330]

The first call fixes the VAT rate and returns a unary function that accepts only the price. That unary shape is exactly what map or pipe wants.

Boundaries & Distinctions

Currying and partial application are closely related but not identical. Currying is a structural transformation: it changes a function from 'many arguments at once' into 'one argument at a time.' Partial application is a usage pattern: you supply some arguments now and get a new function for the rest later. Curried functions are especially easy to partially apply, but non-curried functions can also be partially applied with wrappers or bind. Currying changes the function's shape; partial application changes how you use the function.

Trade-off

Currying improves composition and reuse because it makes function arity predictable and pipeline-friendly. The cost is that the call style becomes less familiar to teams used to ordinary multi-argument calls, and argument order suddenly matters much more because the earliest arguments become the easiest to fix and reuse. Currying pays off when the same function will be reused in several pipelines or pre-configured in several ways. If the function is almost always called with all arguments at once, the extra staging may add ceremony without much benefit.

When should you use it?

Currying is most effective for validators, formatters, selectors, and request builders where configuration naturally comes first and real data arrives later. It is especially helpful in codebases that rely on pipe, compose, and higher-order collection utilities, because it converts awkward multi-argument functions into clean unary stages. The practical design question is: will this function be reused as a pipeline step? If the answer is yes, currying often lowers the friction of every later call site.

Composition pipelines -- reshaping multi-argument functions so they fit cleanly into pipe and composeReusable validators -- fixing one rule up front, then reusing the resulting unary function on many valuesFormatter factories -- supplying locale, prefix, or rounding policy step by stepFunctional utility libraries -- designing Ramda-style APIs that compose without extra wrappers