DEV Community

Rahul Jindal
Rahul Jindal

Posted on

Currying in JavaScript

Currying is a functional programming technique that involves transforming a function with multiple arguments into a series of functions that each take a single argument.

In JavaScript, currying involves breaking down a function that takes multiple arguments into a series of functions, each of which takes a single argument. The first function returns another function that takes the next argument, and so on, until all arguments have been passed to the function. The final function then returns the result.

Here is an example of normal function in JavaScript:

function add(a, b) {
  return a + b;
}
Enter fullscreen mode Exit fullscreen mode

Here is an example of a curried function in JavaScript:

function add(a) {
  return function(b) {
    return a + b;
  };
}
Enter fullscreen mode Exit fullscreen mode

Prerequisite

Understanding closures is a pre-requisite to understanding currying in JavaScript. Currying leverages the closure property of functions in JavaScript to capture arguments and build up a complete set of arguments over time. By using closures, currying allows you to partially apply arguments to a function, and create new functions with a fixed set of arguments that can be called later with the remaining arguments. Without a solid understanding of closures, it can be difficult to understand how currying works and why it is useful.


Benefits of Currying

1 Reusable functions: Currying allows you to create reusable functions that can be partially applied with some arguments and then used multiple times with different arguments.

const add = (a) => (b) => a + b;

const add5 = add(5);
const add10 = add(10);

console.log(add5(3)); // 8
console.log(add10(3)); // 13
Enter fullscreen mode Exit fullscreen mode

2 Readability: Currying can make code easier to read and understand by breaking down complex functions into smaller, more manageable functions.

const add = (a) => (b) => (c) => a + b + c;

const add5 = add(5);
const add5And10 = add5(10);

console.log(add5And10(3)); // 18
Enter fullscreen mode Exit fullscreen mode

3 Composition: Currying allows you to compose functions together to create new functions, making it easier to write and manage code.

const add = (a) => (b) => a + b;
const multiply = (a) => (b) => a * b;

const add5 = add(5);
const double = multiply(2);

const doubleAndAdd5 = (x) => add5(double(x));

console.log(doubleAndAdd5(3)); // 11
Enter fullscreen mode Exit fullscreen mode

The use of currying in this example makes the code more composable and maintainable by breaking down complex functions into smaller, simpler functions that can be composed together to create the final result.

4 Easier testing: Functions that are curried are easier to test, as you can test each individual function in isolation.

5 Better code organization: By breaking down functions into smaller, more manageable functions, currying can help to improve the organization of your code.

const add = (a) => (b) => a + b;
const multiply = (a) => (b) => a * b;

const add10 = add(10);
const multiplyBy5 = multiply(5);

const add10AndMultiplyBy5 = (n) => add10(multiplyBy5(n));

console.log(add10AndMultiplyBy5(2)); // 20
Enter fullscreen mode Exit fullscreen mode

The use of currying in this example improves code organization by breaking down the simple add and multiply functions into smaller, more manageable functions that can be composed together in a single expression to achieve the desired result. This can help to make the code more readable, maintainable, and reusable.

6 Improved performance: Currying can lead to improved performance, as partially applied functions can be optimized and cached, reducing the number of function calls and improving performance.

const memoizedFetch = (() => {
  const cache = {};
  return (url) => {
    if (cache[url]) return Promise.resolve(cache[url]);
    return fetch(url)
      .then((response) => response.json())
      .then((data) => {
        cache[url] = data;
        return data;
      });
  };
})();

const fetchUsers = memoizedFetch("https://jsonplaceholder.typicode.com/users");
const fetchPosts = memoizedFetch("https://jsonplaceholder.typicode.com/posts");
const fetchTodos = memoizedFetch("https://jsonplaceholder.typicode.com/todos");

fetchUsers.then(console.log); // [{...}, {...}, ...]
fetchPosts.then(console.log); // [{...}, {...}, ...]
fetchTodos.then(console.log); // [{...}, {...}, ...]
Enter fullscreen mode Exit fullscreen mode

The use of currying in this example improves performance by allowing us to cache the results of previous requests and avoid making the same request multiple times. This can be particularly useful when the request is time-consuming or has a high latency, as it allows us to cache the result and reuse it whenever possible.

7 Better function composition: When functions are curried, they can be composed in a more flexible way, making it easier to write complex functions and making the code more maintainable.

const multiply = (a) => (b) => a * b;
const add = (a) => (b) => a + b;
const modulo = (a) => (b) => b % a;

const isOdd = modulo(2);
const double = multiply(2);
const increment = add(1);

const processNumber = (num) =>
  double(increment(isOdd(num) ? num : increment(num)));

console.log(processNumber(2)); // 6
console.log(processNumber(3)); // 8
Enter fullscreen mode Exit fullscreen mode

Applications of Currying

1 Event handling: Currying can be used to simplify event handling in a program by partially applying event handlers with specific arguments, making it easier to handle similar events in a generic way.

const handleClick = (elementId) => (event) => {
  console.log(`Clicked on element with id: ${elementId}`);
};

const button1 = document.getElementById('button1');
button1.addEventListener('click', handleClick('button1'));

const button2 = document.getElementById('button2');
button2.addEventListener('click', handleClick('button2'));
Enter fullscreen mode Exit fullscreen mode

2 API calls: Currying can be used to abstract API calls and make them more flexible and reusable by partially applying them with specific parameters, reducing the need to repeat the same code for similar API calls.

3 Data processing: Currying can be used to simplify data processing pipelines by partially applying functions with specific parameters, making it easier to process data in a modular and composable way.

const curry = (fn) => {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return function(...args2) {
        return curried.apply(this, [...args, ...args2]);
      };
    }
  };
};

const filterData = curry((fn, data) => data.filter(fn));
const filterNumbers = filterData((x) => typeof x === "number");
const result = filterNumbers([1, "hello", 3, "world", 5]); 

console.log(result); // [1, 3, 5]
Enter fullscreen mode Exit fullscreen mode

Conclusion

In conclusion, currying is a concept from functional programming that involves transforming a function with multiple arguments into a sequence of functions, each taking a single argument. Currying can make it easier to reuse and compose functions, leading to more maintainable and readable code. While not directly useful in user interface development, it can be a helpful concept in functional programming that can lead to improved code quality. Whether or not to use currying in a project depends on the specific needs and constraints of that project, and its use should be evaluated based on the specific requirements.

Top comments (0)