In this article I will tell you about different types of currying in JavaScript. To understand them, you will need to know about classical currying. You can read my article on that topic and then proceed with this one.

I don’t need the whole world, just you

Let’s look at the example of a curry function in JavaScript:

const curry = (fn) =>     
    function curried(...args) {
        const haveEnoughArgs = args.length >= fn.length        
        const partiallyApplied = (...moreArgs) => 
            curried(...args.concat(moreArgs))   

        if (haveEnoughArgs) return fn(...args)        
        else return partiallyApplied    
    }

It returns a function that can handle cases like this:

  • curry(someFunction(1)(2)(3))
  • curry(someFunction(1, 2, 3))
  • curry(someFunction(1, 2)(3))
  • curry(someFunction(1)(2))

What we would like to add to this list?

Well, what if we want to call our curried function with less/more arguments than its requiring?

For example, we have a function that can add up numbers infinitely:

const addInfinite = (...args) => args.reduce((acc, cur) => acc + cur, 0)

We want to curry it and then add up numbers one or more at a time from some infinite stream. We don’t know when we’ll run out of numbers to add, but we know that we only need 10 of them. Let’s say, we are trying to compute some statistical indicator in real-time. So we need to update it as soon as possible and 10 data-points will be enough.

Cool. Let’s try it. We curry and feed it our first number from a stream:

curry(addInfinite)(5) // returns 5

Wait, it returned a value instead of a partially applied function. Now we can’t feed it other numbers. But why?

JavaScript registers our function’s arity as 0 since it can take any number of arguments. So, if we curry it with our classical curry function and call it with anything, it returns right away. Even like this:

curry(addInfinite)() // returns 0

How do we fix this?

We can use currying to fixed arity!

Currying to fixed arity returns a curried function that expects a certain number of arguments to return a value.

So now:

curryN(10, addInfinite)(5)(2)(8) // returns partially applied function
curryN(10, addInfinite)(5)(2)(8)(5)(2)(1)(8)(3)(4)(5) // returns 43

Great, we got what we wanted!

Now let’s see how to write this function. Well, it will be easy. Currying to a fixed arity is the same as classical currying, but instead of getting desired arity from the function itself we just set it explicitly.

Basically, classical currying is a special case of currying to fixed arity.

And we already have the code for classical arity. So, we just change it a bit:

const curryN = (n, fn) =>    
    function curried(...args) {
        // compare to n instead of fn.length
        const haveEnoughArgs = args.length >= n      
        const partiallyApplied = (...moreArgs) => 
            curried(...args.concat(moreArgs))   

        if (haveEnoughArgs) return fn(...args)        
        else return partiallyApplied    
    }

That’s it! We have ourselves a new function that does currying to a fixed arity.

And since we have established classical currying as a special case of that mechanism, we can now rewrite our classical curry function and get rid of code duplication:

export const curry = (fn) => curryN(fn.length, fn)

Nice!

#functional-programming #software-development #front-end-development #programming #javascript

Variadic currying and currying to fixed arity in JavaScript
1.60 GEEK