Search code examples
javascriptfunctionhigher-order-functionsfirst-class-functions

How to write a JavaScript function that works both as a first-class function and higher-order function?


For example let's take the function add. I want to be able to call:

add(1, 2)(3, 4)

and

add(1, 2)

using same implementation. I have tried writing the add function the following way:

var add = (a, b) => {
    return (c, d) => {
         return a + b + c + d;
    }
}

This will work fine for add(1, 2)(3, 4) but it will not work for add(1, 2). I am not sure how can I check if a function is passed arguments before returning it.


Solution

  • As there is no way for a function (when called) to know whether its return value will also be called like a function or not, the best you can do is to let the return value be callable and have a valueOf method, so that it can be used directly as a number in a larger expression that expects a number:

    function add(...args) {
        let sum = 0;
        const f = function (...args) {
            for (const val of args) sum += val;
            return f;
        }
        f.valueOf = () => sum;
        return f(...args);
    }
    
    console.log(+add(1, 2)(3)(4, 5));

    The "trick" here is that the + operator forces its operand to coerce to a number, and this will implicitly call the valueOf method of the function-object that add evaluates to. That + operator is just one of the many ways to get that valueOf method called. A few more examples:

    console.log("result = " + add(1, 2)(3)(4, 5));
    console.log(add(1, 2)(3)(4, 5) - 0);
    console.log(add(1, 2)(3)(4, 5) * 1);
    console.log(Number(add(1, 2)(3)(4, 5)));