Academic Block

Currying Get Certified

Currying is a technique where a function that takes multiple arguments is transformed into a sequence of functions that each take a single argument. Instead of calling add(1, 2, 3), a curried version is called as add(1)(2)(3).

Manual Currying

What this code does: Manually turns a multi-argument function into a chain of single-argument functions.

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

console.log("add(1)(2)(3) =", add(1)(2)(3));

const addOne = add(1);
const addOneAndTwo = addOne(2);
console.log("Calling in stages:", addOneAndTwo(3));

Writing a Generic curry() Helper

What this code does: Builds a reusable curry() helper that works on any function using fn.length.

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));
        };
    };
}

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

const curriedMultiply = curry(multiply);

console.log("All at once:", curriedMultiply(2, 3, 4));
console.log("One at a time:", curriedMultiply(2)(3)(4));
console.log("Mixed:", curriedMultiply(2, 3)(4));

Currying Enables Partial Application

What this code does: Shows how currying lets you lock in some arguments early to create specialized functions.

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));
        };
    };
}

function formatMessage(level, prefix, message) {
    return "[" + level + "] " + prefix + ": " + message;
}

const curriedFormat = curry(formatMessage);

// Lock in "level" and "prefix"
const logError = curriedFormat("ERROR", "App");
const logWarning = curriedFormat("WARNING", "App");

console.log(logError("Failed to connect to database"));
console.log(logWarning("Deprecated function used"));
console.log(logError("Timeout while fetching data"));

Currying vs Plain Partial Application

What this code does: Contrasts true currying with simple partial application using bind().

function volume(length, width, height) {
    return length * width * height;
}

// Partial application with bind()
const baseVolume = volume.bind(null, 10);
console.log("Partial application:", baseVolume(5, 2));

// Currying
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 curriedVolume = curry(volume);
console.log("Curried, one at a time:", curriedVolume(10)(5)(2));

Practical Use Case: Building Reusable Utilities

What this code does: Uses currying to create specialized filter and map helpers that are easy to reuse.

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 filter = curry((predicate, array) => array.filter(predicate));
const map = curry((transform, array) => array.map(transform));
const greaterThan = curry((min, n) => n > min);
const multiplyBy = curry((factor, n) => n * factor);

const numbers = [1, 5, 10, 15, 20, 25];

const keepAboveTen = filter(greaterThan(10));
const doubleAll = map(multiplyBy(2));

console.log("Above ten:", keepAboveTen(numbers));
console.log("Doubled:", doubleAll(numbers));
console.log("Both combined:", doubleAll(keepAboveTen(numbers)));

Currying vs Regular Functions

Feature Regular Function Curried Function
How arguments are passed All at once One at a time (or grouped)
Return value per call The final result Another function until fully applied
Supports partial application? Not natively Yes, inherently
Good for Simple one-shot calls Building reusable specialized functions

Common Mistakes

  • Currying functions that use default or rest parameters (fn.length is wrong)
  • Over-currying simple functions that don’t need it
  • Logging intermediate results and seeing “[Function]”
  • Losing this when currying methods
  • Thinking currying improves performance (it doesn’t)

Best Practices

  • Use currying when you repeatedly write similar functions that differ by only a few arguments
  • Keep curried functions pure (no side effects)
  • Prefer a generic, well-tested curry helper
  • Order parameters from most general to most specific
  • Don’t force currying on every function

Quick Summary

  • Currying turns a multi-argument function into a chain of single-argument functions
  • A generic curry() helper uses fn.length to know when to call the original function
  • It enables partial application — locking in arguments to create specialized functions
  • Best for building small, reusable, composable utilities

Try It Yourself

What this code does: Curries a URL builder and creates a specialized helper for one protocol and domain.

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));
        };
    };
}

function buildUrl(protocol, domain, path) {
    return protocol + "://" + domain + "/" + path;
}

const curriedBuildUrl = curry(buildUrl);

// Lock in protocol and domain
const onMySite = curriedBuildUrl("https", "example.com");

console.log(onMySite("home"));
console.log(onMySite("about"));
console.log(curriedBuildUrl("http", "test.local", "debug"));
Tip: If you keep writing near-identical functions that only differ by their first argument or two, a curried version will usually let you build them all from one general function.

🧪 Test Your JavaScript Code

Edit the code on the left and click “Run Code” to see the console output on the right.

📝 JavaScript Code
👁 Console Output

Click “Run Code” to see the output here.

Ctrl + Enter to run code Esc to close