Functional Programming in JavaScript: Partial Application and Currying

Functional programming (FP) is a paradigm that can transform your JavaScript code into a more predictable, modular, and maintainable form. In this blog, we’ll explore functional programming, dive deep into partial application and currying, and explain their benefits and use cases. Whether you're preparing for a frontend role or aiming to write cleaner code, this blog will help you understand these concepts with clear examples.


What is Functional Programming?

Functional programming emphasizes pure functions, immutability, higher-order functions, and avoiding side effects. In JavaScript, FP leverages functions as first-class citizens, enabling elegant and reusable code.

Key Principles of Functional Programming

  1. Pure Functions: Functions that produce the same output for the same input and have no side effects.

     function add(a, b) {
       return a + b;
     }
     console.log(add(2, 3)); // 5 (always, no side effects)
    
  2. Immutability: Data is not modified; new data is created instead.

     const numbers = [1, 2, 3];
     const doubled = numbers.map(n => n * 2); // [2, 4, 6]
     console.log(numbers); // [1, 2, 3] (original unchanged)
    
  3. Higher-Order Functions: Functions that accept or return other functions.

     const numbers = [1, 2, 3];
     const square = n => n * n;
     console.log(numbers.map(square)); // [1, 4, 9]
    
  4. Avoiding Side Effects: Functions avoid modifying external state.

     // Impure (side effect)
     let total = 0;
     function addToTotal(n) {
       total += n;
       return total;
     }
    
     // Pure
     function sumArray(arr) {
       return arr.reduce((acc, n) => acc + n, 0);
     }
    
  5. Function Composition: Combining simple functions to create complex ones.

     const addOne = x => x + 1;
     const multiplyByTwo = x => x * 2;
     const compose = (f, g) => x => f(g(x));
     const addOneThenMultiply = compose(multiplyByTwo, addOne);
     console.log(addOneThenMultiply(5)); // (5 + 1) * 2 = 12
    

Why Use Functional Programming?

  • Predictability: Pure functions simplify testing and reasoning.

  • Maintainability: Immutability reduces bugs from state changes.

  • Reusability: Higher-order functions enable modular code.

  • Scalability: FP promotes clean codebases for large applications.

  • Concurrency: Immutability suits parallel processing.

Use Cases in JavaScript

  • Data Transformations: Using map, filter, and reduce for API data.

  • Event Handling: Functional approaches in React or Vue.

  • State Management: Libraries like Redux rely on FP principles.


What is Partial Application?

Partial application is a technique where you fix some arguments of a function to create a new function that expects the remaining arguments. It reduces the number of arguments (arity) a function takes, making it more specialized and reusable.

Simple Example

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

function partialAdd(a) {
  return function(b, c) {
    return add(a, b, c);
  };
}

const addFive = partialAdd(5);
console.log(addFive(2, 3)); // 5 + 2 + 3 = 10

Why Use Partial Application?

  • Reusability: Create specialized functions (e.g., addFive) from generic ones.

  • Simplicity: Pre-configure arguments for specific contexts.

  • Modularity: Simplifies complex functions for easier use.

Use Cases

  1. API Calls:

     function fetchData(baseUrl, endpoint) {
       return fetch(`${baseUrl}/${endpoint}`);
     }
     const apiFetch = partial(fetchData, 'https://api.example.com');
     apiFetch('users'); // Fetches from https://api.example.com/users
    
  2. Event Handlers:

     function logEvent(event, message) {
       console.log(`${event}: ${message}`);
     }
     const logClick = partial(logEvent, 'click');
     logClick('Button pressed'); // click: Button pressed
    

Generic Partial Application Utility

function partial(func, ...fixedArgs) {
  return function(...remainingArgs) {
    return func(...fixedArgs, ...remainingArgs);
  };
}
const addTen = partial(add, 10);
console.log(addTen(2, 3)); // 15

What is Currying?

Currying transforms a function with multiple arguments into a sequence of single-argument functions. Instead of calling f(a, b, c), you call f(a)(b)(c). Each function returns a new function until all arguments are provided. Currying leverages JavaScript’s closures or the bind method to "remember" earlier arguments.

Currying with Closures

Closures allow a function to retain access to variables from its outer scope, making them ideal for currying. Here’s an example:

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

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

console.log(curriedAdd(1)(2)(3)); // 6

Explanation: Each inner function "closes over" the previous arguments (a, then b), using them when the final argument (c) is provided.

Currying with Arrow Functions

Arrow functions provide a concise syntax for currying:

const curriedAdd = a => b => c => a + b + c;
console.log(curriedAdd(1)(2)(3)); // 6

Arrow functions eliminate explicit function and return keywords, making curried code more readable and declarative.

Currying with Bind

JavaScript’s bind method can create curried functions by pre-setting arguments:

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

const curriedAdd = add.bind(null); // No context needed
const addFirst = curriedAdd.bind(null, 1); // Fix a = 1
const addSecond = addFirst.bind(null, 2); // Fix b = 2
console.log(addSecond(3)); // 1 + 2 + 3 = 6

Explanation: bind creates a new function with some arguments pre-set. Each bind call fixes one argument, mimicking currying’s single-argument chain.

Advantages of Currying

  • Higher-Order Functions: Currying enables the creation of higher-order functions that can be passed to other functions like map, filter, or reduce.

  • Single Responsibility: It divides a function into multiple smaller functions, each handling one responsibility, reducing the chance of errors.

  • Modular and Reusable Code: Curried functions can be reused by fixing arguments incrementally, promoting modularity.

  • Avoid Passing Same Variables: Currying eliminates the need to repeatedly pass the same variables across function calls.

  • Improved Readability: By breaking down complex logic into smaller, single-argument functions, currying makes code more readable.

When to Use Currying in JavaScript?

Currying is particularly useful in the following scenarios:

  • Partial Application: Set some arguments in advance and call the function later with the remaining arguments, creating specialized functions.

  • Higher-Order Functions: Manage arguments effectively when functions are passed to other functions (e.g., map, filter, reduce).

  • Functional Programming: Currying aligns with FP principles, focusing on immutability and function composition in scenarios where data shouldn’t change.

Currying vs. Partial Application

  • Currying: Produces a chain of single-argument functions (f(a)(b)(c)).

      const curried = a => b => c => a + b + c;
      console.log(curried(1)(2)(3)); // 6
    
  • Partial Application: Produces a function with fewer arguments, which can take multiple arguments at once.

      const partial = a => (b, c) => a + b + c;
      console.log(partial(1)(2, 3)); // 6
    

Relationship: Currying is a specific form of partial application, where arguments are applied one at a time through nested single-argument functions.


Conclusion

Partial application and currying are powerful functional programming techniques in JavaScript that enhance code modularity, reusability, and predictability. Partial application creates specialized functions by fixing some arguments, while currying breaks functions into single-argument chains using closures or bind, offering flexibility and deferred execution.

Thanks for reading!
👉 Check out our companion blog – Practical Examples of Currying in JavaScript to see currying in action, with real-world use cases like logging, DOM manipulation, and more.
Did you try any? Share your thoughts on functional programming in the comments!

0
Subscribe to my newsletter

Read articles from Deepthi Purijala directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Deepthi Purijala
Deepthi Purijala

Full Stack Developer with hands-on experience of more than 1 year. Proficient in both Back-end and Front-end technologies, with a strong commitment to delivering high-quality code