DEV Community

Cover image for Currying in JS 🤠
Marcos Henrique
Marcos Henrique

Posted on • Updated on

Currying in JS 🤠

Cooking with javascript? What? 🤷‍♂️


Currying is a technique, where a function takes several parameters as input and returns a function with exactly one parameter.

Currying is a fundamental tool in functional programming, a programming pattern that tries to minimize the number of changes to a program’s state (known as side effects) by using immutable data and pure (no side effects) functions.

Now, let's cut to the chase. Check this code example:
const isDivisible = (divider, number) => !(number % divider);
const divider = 2;

console.log(isDivisible(divider, 40)); // true
console.log(isDivisible(divider, 33)); // false
Enter fullscreen mode Exit fullscreen mode

In the previous code, isDivisible is a function expression that checks whether one number is divisible by another, returning true or false, as simple as that.

If my intention in the three isDivisible calls is to use the same divisor, isn't it tedious and tedious having to pass the divisor as a parameter every time I want to know if a number is divisible by two?

Just changing the isDivisible function and making the divisor a fixed value.
But we would have a gigantic coupling impacting a non-reusable and fully cast function

And now is the time for our game star to step in

Currying 🤓

const isDivisible = divider => number => !(number % divider);

console.log(isDivisible(2)(40)); // true
console.log(isDivisible(2)(33)); // false
console.log(isDivisible(3)(40)); // false
console.log(isDivisible(3)(33)); // true
Enter fullscreen mode Exit fullscreen mode


Therefore now we have a decoupled and flexible function, not only dependent on number two and can be used in any situation we want to know if a number is divisible or not 🧐

🍻

Top comments (11)

Collapse
 
aminnairi profile image
Amin • Edited

Hi there and thanks for your article!

In my opinion, this article deserves a little bit more explanations, especially the usefulness of using curried functions.

If I start from your example.

const isDivisible = divider => number => !(number % divider);

console.log(isDivisible(2)(40)); // true
console.log(isDivisible(2)(33)); // false
console.log(isDivisible(3)(40)); // false
console.log(isDivisible(3)(33)); // true
Enter fullscreen mode Exit fullscreen mode

Even though you have successfully curried your function, we can see that the 2 & 3 parameters could have been reused. Now if I take this example.

const isDivisible = divider => number => !(number % divider);

console.log(isDivisible(2)(40)); // true
console.log(isDivisible(2)(33)); // false
console.log(isDivisible(2)(29)); // false
console.log(isDivisible(2)(26)); // true
Enter fullscreen mode Exit fullscreen mode

We can see that I'm repeating the 2 a lot. What is great about curried functions is that they can be used to compose higher order functions (a higher order function is a function that either takes a function as parameter or returns a new one), and so helping us reuse some parameters that are commons accross function calls.

const isDivisible = divider => number => !(number % divider);
const isDivisibleByTwo = isDivisible(2);

console.log(isDivisibleByTwo(40)); // true
console.log(isDivisibleByTwo(33)); // false
console.log(isDivisibleByTwo(26)); // true
console.log(isDivisibleByTwo(39)); // false
Enter fullscreen mode Exit fullscreen mode

And this is, in my opinion, what makes currying so powerful. Composing functions helps you decrease the needs for repeating common parts of your application.

Collapse
 
lbayliss profile image
Luke Bayliss

This is what I was expecting to read!

Collapse
 
wakeupmh profile image
Marcos Henrique

Thanks for the explanation,I tried to introduce the characteristic of flexibility, although this approach that you said was the most common as to the power of currying.

Collapse
 
fjones profile image
FJones • Edited

Another (arguably more important in practical use) aspect of currying is that enforcing a single argument to the final produced function allows for greater automation in the evaluation.

Consider nested function calls or array operations. While currying is less about minimizing side effects (nothing prevents the curried functions from being stateful - in fact one might argue that currying can encourage stateful functions), it is certainly more aligned with a functional paradigm to write, say arr.filter(isDivisibleByTwo) over arr.filter((v) => isDivisible(2, v)).

Collapse
 
gypsydave5 profile image
David Wickes

CHALLENGE

Write a function that curries a function. It should take a function as an argument, and return the curried version.

NEXT LEVEL

Now write a function that uncurries a function.

Collapse
 
aminnairi profile image
Amin
"use strict";

/**
 * Curry a function of any arity
 * 
 * @param {Function} callable
 * @param {unknown[]=} [] initialParameters
 * @return {Function}
 */
function curry(callable, ...initialParameters) {
    return function(...additionalParameters) {
        const parameters = [...initialParameters, ...additionalParameters];

        if (parameters.length >= callable.length) {
            return callable(...parameters);
        }

        return curry(callable, ...parameters);
    };
}

/**
 * Uncurry a function of any arity
 * 
 * @param {Function} curriedFunction
 * @return {Function}
 */
function uncurry(curriedFunction) {
    return function(...parameters) {
        return parameters.reduce(function(next, value) {
            return next(value);
        }, curriedFunction);
    }
}

const add = curry((x, y) => x + y);
const increment = add(1);

console.log(add);           // [Function]
console.log(increment);     // [Function]
console.log(add(1, 2));     // 3
console.log(increment(2));  // 3

const $add = uncurry(add);

console.log($add);          // [Function]
console.log($add(1, 2));    // 3
Collapse
 
pnu profile image
Panu Ervamaa • Edited
const curry = (fn, ...args) =>
  args.length >= fn.length
    ? fn(...args)
    : (...x) => curry(fn, ...args, ...x);

const uncurry = (fn) => (...args) =>
  args.reduce((fn, arg) => fn(arg), fn);

const mul = (a, b) => a * b;
const curriedMul = curry(mul);
const mulBySeven = curriedMul(7);
const uncurriedMul = uncurry(curriedMul);

console.log(curriedMul(2)(3));    // 6
console.log(mulBySeven(11));      // 77
console.log(uncurriedMul(5, 7));  // 35
Collapse
 
kmwill23 profile image
Kevin

Neat concept, but the readability on it is a bit questionable.

Collapse
 
arthurbarbero profile image
Arthur Barbero

Incrível!!! Awesome, I didn't know that the arrow function could return other arrow function!

Collapse
 
rolandcsibrei profile image
Roland Csibrei

Exactly...

Collapse
 
wakeupmh profile image
Marcos Henrique

Yeah, it's a technique, but why this isn't about minimizing side effects?