# What Are Currying and Function Composition? A Guide to Modular JavaScript Code

> Learn about currying and function composition in JavaScript. Discover how these functional programming techniques create modular, reusable code by transforming functions into data pipelines.

- Repository: [Leonardo Maldonado/33-js-concepts](https://github.com/leonardomso/33-js-concepts)
- Tags: deep-dive
- Published: 2026-03-03

---

**Currying and function composition are functional programming techniques that break complex logic into reusable, single-purpose units by transforming multi-argument functions into chains of unary functions and wiring them together into explicit data pipelines.**

The *33-js-concepts* repository by leonardomso demonstrates these patterns with production-ready implementations in [`tests/functional-programming/currying-composition/currying-composition.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/tests/functional-programming/currying-composition/currying-composition.test.js), showing how they eliminate boilerplate and create declarative code flows.

## Understanding Currying in JavaScript

### What Is Currying?

**Currying** converts a function that accepts multiple arguments into a sequence of nested functions, each taking exactly one argument. Each intermediate function returns another function awaiting the next argument until all parameters are satisfied.

In the 33-js-concepts codebase, a manual curried function looks like this:

```javascript
// Basic currying – one argument at a time (L7-L8)
const add = a => b => c => a + b + c;

add(1)(2)(3); // → 6

```

This pattern enables **partial application**, where you fix some arguments upfront and reuse the resulting specialized function later.

### The Curry Implementation in 33-js-concepts

The repository provides a generic `curry` helper that works with functions of any arity. Located at lines 93–101 in [`currying-composition.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/currying-composition.test.js), the implementation recursively accumulates arguments until the original function's length is met:

```javascript
// Arbitrary-arity curry helper (L93-L101)
const curry = fn => {
  return function curried(...args) {
    return args.length >= fn.length
      ? fn(...args)
      : (...next) => curried(...args, ...next);
  };
};

const sum = (a, b, c) => a + b + c;
const curriedSum = curry(sum);

curriedSum(1)(2)(3); // → 6
curriedSum(1, 2)(3); // → 6 (flexible invocation)

```

A simpler two-argument version appears at lines 75–84 for cases where you only need binary operations.

### Partial Application with Curried Functions

Once a function is curried, you can create reusable presets by supplying initial arguments. According to the test suite at lines 87–89, this pattern replaces repetitive inline logic:

```javascript
// Reusing a curried function (L87-L89)
const add5 = add(5);
add5(10); // → 15
add5(20); // → 25

```

This technique appears throughout the companion file [`tests/functional-programming/vanilla-js-utility-functions/vanilla-js-utility-functions.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/tests/functional-programming/vanilla-js-utility-functions/vanilla-js-utility-functions.test.js), where curried utilities like `map` and `filter` are composed into domain-specific operations.

## Function Composition Patterns

### Left-to-Right with Pipe

**Function composition** chains unary functions so the output of one becomes the input of the next. The `pipe` helper (lines 35–36) executes functions left-to-right, matching natural reading order:

```javascript
// Pipe – left-to-right composition (L35-L36)
const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x);

const process = pipe(
  x => x + 1,
  x => x * 2,
  x => x * x
);

process(5); // → 144 (5 → 6 → 12 → 144)

```

### Right-to-Left with Compose

For "data-last" thinking, the repository offers `compose` (lines 64–65), which folds functions from right to left using `reduceRight`:

```javascript
// Compose – right-to-left composition (L64-L65)
const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);

const composed = compose(
  x => x * x,
  x => x * 2,
  x => x + 1
);

composed(5); // → 144 (same calculation, reversed order)

```

## Building Modular Code with Currying and Composition

### Reusable Utility Functions

Combining curried helpers with pipelines creates declarative data transformations without inline boilerplate. The vanilla JS utilities test file demonstrates this with array operations:

```javascript
// Curried utilities ready for composition
const map = fn => arr => arr.map(fn);
const filter = pred => arr => arr.filter(pred);

const double = x => x * 2;
const isEven = x => x % 2 === 0;

const doubleEvens = pipe(
  filter(isEven),
  map(double)
);

doubleEvens([1, 2, 3, 4]); // → [4, 8]

```

Here, `map` and `filter` are curried to accept their behavioral argument first, returning functions awaiting the data array. This data-last style maximizes reusability across different collections.

### Real-World API Client Example

The test suite includes a practical URL builder (lines 40–43) showing how currying creates configurable API clients:

```javascript
// Data-last API client configuration (L40-L43)
const createApiUrl = base => endpoint => params => {
  const qs = new URLSearchParams(params).toString();
  return `${base}${endpoint}${qs ? '?' + qs : ''}`;
};

const github = createApiUrl('https://api.github.com');
const usersEndpoint = github('/users');

usersEndpoint({ per_page: 10 }); // → 'https://api.github.com/users?per_page=10'

```

By currying `createApiUrl`, you lock in the base domain once, then reuse the specialized functions for different endpoints and query parameters without repeating the base URL logic.

## Summary

- **Currying** transforms multi-argument functions into sequences of single-argument functions, enabling partial application and argument presetting as shown in [`currying-composition.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/currying-composition.test.js) lines 93–101.
- **Function composition** via `pipe` (L35–L36) and `compose` (L64–L65) creates explicit data pipelines that read like natural language, making control flow obvious.
- Together, these techniques generate **tiny, single-purpose functions** that combine without rewriting boilerplate, improving reusability across the codebase.
- The 33-js-concepts repository provides canonical implementations that work with arbitrary function arities and integrate seamlessly with JavaScript's native array methods.

## Frequently Asked Questions

### What is the difference between currying and partial application?

**Currying** is the transformation process itself—converting a function of N arguments into N functions of one argument each. **Partial application** is the technique of fixing a number of arguments to a function, producing another function of smaller arity. In the 33-js-concepts examples, currying enables partial application by allowing you to call `add(5)` and get back a reusable function waiting for the remaining arguments.

### When should I use pipe versus compose?

Use **pipe** when you want functions to execute in the order they appear left-to-right, which aligns with how JavaScript reads chronologically. Use **compose** when you prefer right-to-left execution, often matching mathematical notation or "data-last" functional styles. The repository implements both in [`currying-composition.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/currying-composition.test.js) so you can choose based on team conventions or specific algorithm clarity.

### How does currying improve testability?

Curried functions isolate single responsibilities, making unit tests straightforward. Because each step returns a pure function, you can test intermediate transformations independently without mocking entire dependency graphs. The 33-js-concepts test suite itself leverages this modularity to verify `curry`, `pipe`, and `compose` helpers in isolation before testing integrated pipelines.

### Can currying be used with existing JavaScript array methods?

Yes, by wrapping native methods in curried wrappers. The repository demonstrates this in [`vanilla-js-utility-functions.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/vanilla-js-utility-functions.test.js) with patterns like `const map = fn => arr => arr.map(fn)`. This adapts data-first methods to a data-last API, allowing them to slot directly into `pipe` or `compose` chains without imperative restructuring.