DEV Community

Cover image for Currying in JavaScript
Christopher Glikpo
Christopher Glikpo

Posted on

Currying in JavaScript

Currying is a process in functional programming in which we can transform a function with multiple arguments into a sequence of nesting functions. It returns a new function that expects the next argument inline.

NB:The number of arguments a function takes is also called arity.
For example,

function multiply(a, b) {
    // do sth
}
function _multiply(a, b, c) {
    // do sth
}
Enter fullscreen mode Exit fullscreen mode

function multiply takes two arguments (2-arity function) and _multiply takes three arguments (3-arity function).

So,Currying break down a function that takes multiple arguments into a series of functions that each take only one argument.

Here's an example in JavaScript:

function  multiply(x,y,z) {
  return x*y*z;
}
Enter fullscreen mode Exit fullscreen mode

This is a function that takes three arguments, x, y and z and returns their product.

 multiply(1,2,3); // 6
Enter fullscreen mode Exit fullscreen mode

We will now curry this function:

function multiply(x) {
    return (y) => {
        return (z) => {
            return x * y * z
        }
    }
}
console.log(multiply(1)(2)(3)) // 6
Enter fullscreen mode Exit fullscreen mode

Now,we turn a function call multipy(1,2,3) into multipy(1)(2)(3)

To better understand this multiply(1)(2)(3), we may divide it into three parts:

const multi1 = multiply(1);
const multi2 = multi1(2);
const result = multi2(3);
console.log(result); // 6
Enter fullscreen mode Exit fullscreen mode

Now,let's get to know how it works.We passed 1 to the multiply function:

let multi1 = multiply(1);
Enter fullscreen mode Exit fullscreen mode

It returns the function:

return (y) => {
        return (z) => {
            return x * y * z
        }
    }
Enter fullscreen mode Exit fullscreen mode

Now, multi1 holds the above function definition which takes an argument y.
We called the multi1 function, passing in 2:

let multi2 = multi1(2);
Enter fullscreen mode Exit fullscreen mode

The multi1 will return the third function:

return (z) => {
            return x * y * z
        }
Enter fullscreen mode Exit fullscreen mode

The returned function is now stored in multi2 variable.
multi2 will essentially be:

multi2 = (z) => {
            return x * y * z
        }
Enter fullscreen mode Exit fullscreen mode

When multi2 is called with 3 as the parameter,

const result = multi2(3);
Enter fullscreen mode Exit fullscreen mode

It does the computation using the parameters that were previously provided in: x = 1, y = 2, and returns 6.

console.log(result); // 6
Enter fullscreen mode Exit fullscreen mode

The last function only accepts z variable but will perform the operation with other variables whose enclosing function scope has long since returned. Because of Closure, it still functions🔥.

Currying & Partial application 🤔

Some may now believe that the number of nested functions a curried function has is proportional to the number of arguments it receives. That qualifies it as a curry.

Let's take same multiply example:

function multiply(x) {
    return (y) => {
        return (z) => {
            return x * y * z
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

It can be called like this:

let a = multiply(10);
a(3,12);
a(20,12);
a(20,13);

// OR

multiply(10)(3,12);
multiply(10)(20,12);
multiply(10)(20,13);
Enter fullscreen mode Exit fullscreen mode

In contrast to our previous version, which had three arguments and three nesting functions, the above function takes three arguments and contains two nested functions.

This isn't a curry in the traditional sense. We've only used the multiply function in a limited way.

Currying and Partial Application are related (because of closure), but they are of different concepts.

Partial application transforms a function into another function with smaller arity.

function multiply1(x, y, z) {
    return multiply2(x,y,z)
}

// to

function multiply1(x) {
    return (y,z) => {
        return mulitply2(x,y,z)
    }
}
Enter fullscreen mode Exit fullscreen mode

For Currying, it would be like this:

function multiply1(x) {
    return (y) = > {
        return (z) = > {
            return multiply2(x,y,z)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Currying generates nested functions based on the number of arguments passed into the function. Each function is given a parameter. There is no currying if there is no argument.

To create a function that accepts a function and returns a curried function, follow these steps:

function currying(fn, ...args) {
    return (..._arg) => {
        return fn(...args, ..._arg);
    }
}
Enter fullscreen mode Exit fullscreen mode

The above function takes a function (fn) and a variable number of parameters (...args) to curry. After fn, the rest operator is used to collect the number of parameters into...args.

Then, as... args, we return a function that additionally collects the remaining parameters. This function uses the spread operator to call the original function fn, giving in...args and... args as parameters, and then returns the value to the user.

The above code may now be used to build a curry function.

function multiply(x,y,z) {
    return x * y * z
}

let multi = currying(multiply,10);
multi(2,3); // 60
multi(1,4); // 40
Enter fullscreen mode Exit fullscreen mode

Closure makes currying possible in JavaScript. I hope you have learned something new about currying!

If you've reached this point, thank you very much. I hope that this tutorial has been helpful for you and I'll see you all in the next.

If you like my work, please consider
Buy me a coffee
so that I can bring more projects, more articles for you

If you want to learn more about Web Development don't forget to to follow me on Youtube!

Top comments (14)

Collapse
 
darkwiiplayer profile image
𒎏Wii 🏳️‍⚧️ • Edited

Nice article, but you seem to have a somewhat wrong idea of what currying is, and are conflating it with partial application. To clarify:

Partial application

A function is called with less arguments than it needs, and returns a new function that takes the remaining arguments. Some languages support this out of the box:

// Pseudocode. This doesn't work in real javascript.
const mul = (x, y) => x*y
const double = mul(2)
Enter fullscreen mode Exit fullscreen mode

And some, like JS, can only approximate this behaviour using higher-order functions:

const partial(fn, ...args) => (...rest) => fn(...args, ...rest)
const double = partial(mul, 2)
Enter fullscreen mode Exit fullscreen mode

Currying

On the other hand, currying is the process of taking any function and converting it into a series of nested functions of arity 1:

const mul = x => y => x*y
Enter fullscreen mode Exit fullscreen mode

This can sometimes be done automatically, but only in languages that offer some mechanism to inspect a functions arity, and generate a new (nested) function until the right number of arguments has been provided.

// Again, pseudocode. This doesn't actually work in JS.
const mul = curry( (x, y) => x*y)
const double = mul(2)
Enter fullscreen mode Exit fullscreen mode

Just for fun, here's an automatic currying function:

curry = fn => {
    let curried = (...args) => {
        if (args.length >= fn.length)
            return fn(...args)
        else
            return (...rest) => curried(...args, ...rest)
    }
    return curried
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
kishorjoseph profile image
kishorjoseph • Edited

For your above definition of currying how can you say just one arity ? the last function can have any arity. Also, bellow matches to your definition of partial as well.

const add = a => (b, c) => a + b + c
add(1)(2,3)

Collapse
 
linehammer profile image
linehammer

The practical answer is that currying makes creating anonymous functions much easier. Even with a minimal lambda syntax, it's something of a win; compare:

map (add 1) [1..10]
map (\ x -> add 1 x) [1..10]

If you have an ugly lambda syntax, it's even worse. (I'm looking at you, JavaScript, Scheme and Python.)

net-informations.com/js/iq/default...

Collapse
 
darkwiiplayer profile image
𒎏Wii 🏳️‍⚧️

I honestly don't think JS has such a bad lambda syntax; [1,2,3].map(x => 1+x) is still quite acceptable compared to what we have in Lua: function(x) return 1+x end*

* keep in mind that Lua is intentionally minimalistic, making it an easy transpilation target for languages with more convenient syntax, so this is effectively not a big problem

Collapse
 
strubloid profile image
Strubloid

Just to be honest, I got scared of things like that:
const mul = x => y => x*y
This is something readable, but some people can enjoy this "non-hability" of creating variable names, that is the worse thing you can do, but the action of refactoring to become faster, I totally agree, but needs to be well written, and naming is important (even for a an internet example).

Collapse
 
mrandreastoth profile image
Andreas Toth

It's all OK to explain what currying is, but without an explanation as to why one would want to go to all this trouble and extra complexity is necessary to support the article.

Collapse
 
ismailisimba profile image
Ismaili Simba

Thanks, it would be really cool to know if there's some real world case where this is a solution.

Collapse
 
johnkazer profile image
John Kazer

I often use it when I want to use a function in array.map but it takes more than one argument. Supply the initial argument(s) then use the returned function in map.

Thread Thread
 
ismailisimba profile image
Ismaili Simba

That makes a bunch of sense, thanks!

Collapse
 
johnkazer profile image
John Kazer

It's just pure vanilla JavaScript. Fundamental principle of functional programming so any language that treats functions as arguments can do this - not a JavaScript mess 😉

Collapse
 
arsenmazmanyan1104 profile image
Arsen Mazmanyan

Interesting article. Never heard of that. But what about the browser support? And from which EcmaScript is it being supported?

Collapse
 
msamgan profile image
Mohammed Samgan Khan

hi, nice article.
can you please also provide some use case for more clarification.

Collapse
 
ashishk1331 profile image
Ashish Khare😎

This thing is a pure brain fck for Java devs, like me. Rest, please keep unveiling secrets of this js.

Collapse
 
anomaly2009 profile image
Ibne Nahian

AHH ! The JavaScript mess.....