DEV Community

loading...
Cover image for Currying in JS 🤠

Currying in JS 🤠

wakeupmh profile image Marcos Henrique Updated on ・2 min read

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

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


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 🧐

🍻

Discussion (13)

pic
Editor guide
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

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

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

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
lpbayliss profile image
Luke Bayliss

This is what I was expecting to read!

Collapse
wakeupmh profile image
Marcos Henrique Author

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
lukaszahradnik profile image
Lukáš Zahradník • Edited

Currying is a function that 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.

Currying isn't a function and isn't about minimizing side effects

Collapse
rolandcsibrei profile image
Collapse
wakeupmh profile image
Marcos Henrique Author

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

Collapse
lukaszahradnik profile image
Lukáš Zahradník

Currying isn't about the content of function (where side effects happen), but about translating evaluation into a sequence.

Collapse
feichinger 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!