Currying and Composition

Introduction: Building Blocks For Flexible Code

In our previous lessons, we covered how functions can capture state through closures and how pure functions allow us to handle data without causing side effects. You have already seen how to process lists of data using tools like map and reduce. Now, we are going to learn how to snap these concepts together like building blocks.

The goal of this lesson is to make your functions even more flexible and reusable. By the end of this session, you will understand how to break complex functions into smaller steps and how to link those steps together to create a smooth data pipeline. This approach makes your code look less like a series of instructions and more like a clear story of how your data is being transformed.

Understanding Currying

Normally, when you write a function in JavaScript, you give it all its arguments at once. For example, a function to calculate tax might look like calculateTax(rate, amount). Currying is a technique where we change a function so that instead of taking all arguments at one time, it takes them one by one.

When you curry a function, calling it with the first argument returns a new function that waits for the next argument. This is possible because of closures, which we studied in the first lesson. The inner function "remembers" the first value you gave it. This allows you to create specialized versions of a function that are pre-filled with some of the data they need.

A Simple Curry Helper

While we can write curried functions manually, it is helpful to use a utility that can turn a standard function into a curried one. This helper checks how many arguments the original function expects and decides whether to run the function or return another function to collect more data.

JavaScript
"use strict";

const simpleCurry = (fn) => {
  const curried = (...args) =>
    args.length >= fn.length ? fn(...args) : (...rest) => curried(...args, ...rest);
  return curried;
};

In this code, the helper uses the length property of a function to see how many arguments it needs. If the number of arguments provided is enough, it executes the function. If not, it returns a new function that collects the remaining arguments and merges them with the ones already provided.

Note: This simpleCurry implementation is designed for fixed-arity pure functions (functions with a specific, set number of arguments). In real-world production code, you should be aware that fn.length does not count default parameters or rest parameters (...args). It also doesn't handle the this context, though in functional programming, we typically avoid this in favor of pure data transformation.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal