JavaScript Currying Explained: How and Why to Use It

JavaScript currying

JavaScript Currying Explained: How and Why to Use It

Have you ever seen JavaScript code like sum(5)(6) that returns 11 and wondered how it works? This technique, known as currying, is a powerful concept in functional programming. It allows you to transform a function with multiple arguments into a sequence of nested functions, each taking a single argument.

In this guide, we’ll break down currying using modern JavaScript arrow functions, explore its syntax, and demonstrate its practical advantages with clear examples.

What is Function Currying?

Currying is the process of breaking down a function that takes multiple arguments into a series of functions that each take a single argument. Instead of calling sum(5, 6), you call sum(5)(6). The first call returns a new function that is ready to take the next argument.

This technique relies heavily on closures, as the inner function “remembers” the scope of its parent function, allowing it to access the first argument even after the outer function has finished executing.

How Does Currying Work? A Step-by-Step Example

Let’s recreate the sum function from your transcript, starting with a traditional function and refactoring it to a concise arrow function.

1. The Basic Curried Function

function sum(a) {
  return function(b) {
    return a + b;
  };
}

const addFive = sum(5); // Returns a function where a = 5
const result = addFive(6); // Calls the inner function with b = 6
console.log(result); // Output: 11

// Alternatively, call it directly:
console.log( sum(5)(6) ); // Output: 11

2. Refactoring to Arrow Functions

One of the cleanest ways to write curried functions is using ES6 arrow functions. We can condense the above code into a single, elegant line.

// Step-by-step refactoring:
const sum = (a) => {
  return (b) => {
    return a + b;
  };
};

// Remove braces and implicit return:
const sum = (a) => (b) => a + b;

// Final, concise form:
const sum = a => b => a + b;

// Usage remains the same:
console.log( sum(5)(6) ); // Output: 11

When you see multiple arrows (=>), each arrow represents a new inner function. The pattern a => b => c => ... is a hallmark of currying in JavaScript.

JavaScript currying and arrow functions

Practical Use Cases for Currying

Currying isn’t just a clever trick; it has real-world applications that make your code more flexible and reusable.

1. Creating Specialized Functions (Partial Application)

Imagine an e-commerce site where you need to calculate discounts for various products. The discount percentage might change during a sale, but the calculation logic remains the same.

// Curried discount calculator
const calculateDiscount = discountPercentage => originalPrice => {
  return originalPrice - (originalPrice * discountPercentage);
};

// Create specialized discount functions
const getSummerSaleDiscount = calculateDiscount(0.2); // 20% off
const getClearanceDiscount = calculateDiscount(0.5);  // 50% off

// Now, apply these pre-configured discounts to any product
console.log(getSummerSaleDiscount(100)); // Applies 20% to 100 -> 80
console.log(getClearanceDiscount(100));  // Applies 50% to 100 -> 50
console.log(getSummerSaleDiscount(250)); // Applies 20% to 250 -> 200

Advantage: You define the discount logic once and create multiple pre-configured functions, avoiding repetition and making your code cleaner.

2. Event Handling and Configuration

Currying is excellent for creating pre-configured event handlers or API call functions.

// Curried function to create API request functions for a specific base URL
const createApiRequest = baseURL => endpoint => {
  return fetch(`${baseURL}${endpoint}`).then(res => res.json());
};

// Create a specialized function for a specific API
const fetchFromBlogAPI = createApiRequest('https://api.myblog.com');
const fetchFromUserAPI = createApiRequest('https://api.users.com');

// Use the specialized functions
fetchFromBlogAPI('/posts').then(posts => console.log(posts));
fetchFromUserAPI('/users').then(users => console.log(users));

Key Benefits of Currying

Benefit Description Example
Code Reusability Avoid code duplication by creating specialized, pre-configured functions. const addVAT = calculateTax(0.19);
Readability Breaks down complex functions into smaller, single-purpose, logical units. user => post => createPost(user, post)
Flexibility Allows for partial application of arguments, making function composition easier. Using map(addFive) on an array of numbers.

Frequently Asked Questions (FAQ)

Q1: What exactly is function currying?

A: Currying is a technique where a function that takes multiple arguments is transformed into a sequence of nesting functions, each taking a single argument. This enables partial application, where you can fix a number of arguments to create a new, more specific function.

Q2: What will this statement do: const func = a => b => c => a * b * c?

A: This is a curried function for multiplication. It works in steps:

  1. func(2) returns a function: b => c => 2 * b * c.

  2. func(2)(3) returns another function: c => 2 * 3 * c.

  3. func(2)(3)(4) finally calculates 2 * 3 * 4 and returns 24.

Q3: What is the practical usage of currying?

A: The primary practical use is to create helper functions through partial application. This is incredibly useful for:

  • Pre-configuration: Setting up functions with fixed parameters (like discount rates, base URLs, logging levels).

  • Function Composition: Creating new functions by combining smaller, curried functions, which is a key principle in libraries like Redux.

  • Avoiding Repetition: Writing DRY (Don’t Repeat Yourself) code by reusing a base function with some arguments already set.

Conclusion

Currying, especially when combined with arrow functions, is a elegant and powerful feature in JavaScript. It promotes writing cleaner, more modular, and reusable code. By allowing you to break down functions and pre-apply arguments, it opens up new patterns for function composition and application configuration. Start by identifying places in your code where you repeatedly call functions with similar arguments—these are perfect opportunities to implement currying.