Search code examples
javascriptecmascript-6closureshigher-order-functionscurrying

How to implement infinite currying sum values with a callback function with result in javascript?


I have a test where I need to implement a high order function called sum in this case that fulfills the following requirements:

sum(result => {console.log("-> ", result)});// -> prints: -> 0
sum(1)(result => {console.log("-> ", result)});// -> prints: -> 1
sum(1)(2)(result => {console.log("-> ", result)});// -> prints: -> 3
sum(1)(2)(4)(result => {console.log("-> ", result)});// -> prints: -> 7

I did a function in order to get the infinite currying with recursion but I'm a little bit lost with the callback func.

let sum = a => !a ? 0 : b => b ? sum(a+b) : a
console.log(sum()) -> 0
console.log(sum(1)()) -> 1
console.log(sum(1)(2)()) -> 3
console.log(sum(1)(2)(4)()) -> 7

Solution

  • Use a closure to accumulate the sum. An inner function will call the callback with the sum when it detects the argument is a function. Otherwise it will return itself after adapting the sum:

    function sum(arg) {
        let total = 0;
        
        function inner(arg) {
            if (typeof arg === "function") {
                arg(total);
            } else {
                total += arg;
                return inner;
            }
        }
        
        return inner(arg);
    }
    
    sum(result => {console.log("-> ", result)});// -> prints: -> 0
    sum(1)(result => {console.log("-> ", result)});// -> prints: -> 1
    sum(1)(2)(result => {console.log("-> ", result)});// -> prints: -> 3
    sum(1)(2)(4)(result => {console.log("-> ", result)});// -> prints: -> 7

    If it is not intended that the function maintains state, then you can use the this argument to pass on the total:

    "use strict";
    function sum(arg) {
        function inner(arg) {
            if (typeof arg === "function") {
                arg(+this);
            } else {
                return inner.bind(this + arg);
            }
        }
        
        return inner.call(0, arg);
    }
    
    sum(result => {console.log("-> ", result)});// -> prints: -> 0
    sum(1)(result => {console.log("-> ", result)});// -> prints: -> 1
    sum(1)(2)(result => {console.log("-> ", result)});// -> prints: -> 3
    sum(1)(2)(4)(result => {console.log("-> ", result)});// -> prints: -> 7
    
    const foo = sum(0); 
    foo(100)(console.log); // 100 
    foo(1)(console.log); // 1

    Strict mode is advised here, but it will work in sloppy mode as well. In that case the this argument is boxed and unboxed again.