JavaScript: How to construct a Perpetual Curry function.

JavaScript: How to construct a Perpetual Curry function.

In JavaScript Currying is a technique to translate a function which takes multiple arguments into a sequence of functions which takes single argument yet producing the same result.

Why do we need it?

  1. In functional programming, it provides a way of managing arguments to a function. Helps you deal with only one argument at a time.
  2. It decorates your code by reducing the number of arguments supplied to a function directly.

How does it look?

// Simple function with multiple arguments
function sum(a, b) {
    return a + b;
}
console.log(sum(1, 2)); //Output: 3

// Function with returning sequencial function that takes one argument - Curry
function sum(a) {
    return function(b) {
    	return a + b;
    };
}
console.log(sum(1)(2)); //Output: 3
Currying in JavaScript

Notice that instead of returning the sum of a and b we return an anonymous function which takes b as an argument. This gets immediately invoked upon return when we do sum(1)(2).

One has to ask a big question here. How does the scope of variable a available to the inner anonymous function that gets invoked later 🤯?

The answer is very simple, its JavaScript Closures. If you haven't read my previous post about closures, please do.

JavaScript: Closures
Hide your secrets 😉

Perpetual Curry

You can make the above curry technique by infinitely returning a function which takes one argument and accumulate the result till you say its enough. Something like,

sum(1)...(n)() //Output: Sum of n numbers

Let's get to the crux,

function perpetualCurry() {

    return (() => {
    
        return perpetualCurry(); // Recursive
  
    });
}

console.log(perpetualCurry()()()()()()()) // Output: [Function] 
Simplified perpetual curry function

Writing a perpetual curry function is kind of writing a recursive function. The only difference is that for every recursion, our function will return another function. The above function perpetualCurry will always return an anonymous function that can be invoked  immediately. This is how a basic infinite curry is constructed.

Let's make the above perpetualCurry function a bit more useful that does our sum(). Something like,

sum = (x, y) => x + y;
perpetualCurry(sum)(1)(2)(3)(4)(5)(); //Output: 15

Notice how we end the infinite curry, and notice how the first invocation takes a function as an argument.

But before our function gets too complex to understand let do one more stage of problem break down. Let's accumulate all our arguments and return it as an array. Also, try and end the infinite loop and return accumulated arguments.

function perpetualCurry() {

    const curryWrapper = ((...args) => {
    	
        return (() => {
            if (!x) { // If no argument supplied, It ends the infinite curry
                return args;
            }
            return curryWrapper(...args, x); //Recursive, Accumulate args
        })
  
    });
    
    return curryWrapper();
}

perpetualCurry()(1)(2)(3)(4)(5)(); // Output: [1,2,3,4,5]
Accumulating arguments

See how we added another layer of function called curryWrapper(). Notice on our recursion code how we use spread operator and accumulate our arguments. The first invocation is reserved for passing the sum() function that can be used with call or apply to return our desired result.

Let's do our final perpetualCurry() function that will return our desired result.

sum = (x, y) => x + y;


function perpetualCurry(fn) {

    const curryWrapper = ((...args) => {
        return ((i) => {
            if (!i) {
                //Only new thing
                return args.reduce((accmulate, arg) => {
            	    return fn.call(null, accmulate, arg);
                }); 
            }
            return curryWrapper(...args, i);
        })
    });
    
    return curryWrapper(0);
}

perpetualCurry(sum)(1)(2)(3)(4)(5)(); //Output: 15
Final perpetual curry function

The only new addition to the function is the reduce code block with fn.call. The reduce() just accumulates your result by executing our sum.call() by passing two arguments at a time to that function. Finally the reduce() returns the result of all addition when it reaches the end of args array.

If you would like to read more about reduce and call I have added their MDN links at the bottom.

One of the most popular libraries written with this technique is, https://ramdajs.com/

Voila! we reached to the end of this post.

Ideas to further improve

  1. Accommodate more varied data types like inputting other functions, arrays, objects instead of just numbers.
  2. Curry async/await or Promise methods.
  3. Or a combination of all of the above.

Summary

  1. Curry technique can make your code clean and reduce complexity for functional programming as it deals with one argument at a time.
  2. You can extend currying by infinitely returning a curried function.
  3. You can reduce the number of line of code on your project by making the perpetual curry function take any datatype as argument.

References

  1. https://en.wikipedia.org/wiki/Currying
  2. https://codeburst.io/perpetual-currying-in-javascript-5ae1c749adc5
  3. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
  4. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call
Show Comments