Codewars Solution - Functions acting on each other nested

It's a bit complicated. :-)

console.log(eight(minus(three()))); is run from the inside out, so let's follow it through:

  • three() - calls makeNum(3, undefined) and returns what it returns. makeNum(3, undefined) returns 3, so that's the return value.
  • minus(3) - calls minus, passing in 3 as right. minus returns a new function that closes over right.
  • eight(...) - calls makeNum(8, fnFromMinus) where fnFromMinus is the function that was returned by minus(3). makeNum(8, fnFromMinus) does fnFromMinus, passing in 8 as left. fnFromMinus returns the result of left - right (remember that right is 3, fnFromMinus closed over it).

Since 8 - 3 is 5, the final result is 5, which console.log returns.

Here's an instrumented version:

let indent = 0;
function show(label) {
    console.log(" ".repeat(indent * 4) + label);
}
function fname(fn) {
    return fn ? fn.name : "undefined";
}

function makeNum(num, func) {
    const descr = `makeNum(${num}, ${fname(func)})`;
    show(descr);
    ++indent;
    if (func === undefined) {
        --indent;
        show(`${descr} returns ${num}`);
        return num;
    } else {
        const rv = func(num);
        --indent;
        show(`${descr} returns ${num}`);
        return rv;
    }
}

function three(func) {
    const descr = `three(${fname(func)})`;
    console.log(descr);
    ++indent;
    const rv = makeNum(3, func);
    --indent;
    show(descr + ` returns ${rv}`);
    return rv;
}

function eight(func) {
    const descr = `eight(${fname(func)})`;
    console.log(descr);
    ++indent;
    const rv = makeNum(8, func);
    --indent;
    show(descr + ` returns ${rv}`);
    return rv;
}

function minus(right) {
    const fn = function fnFromMinus(left) {
        show(`${fname(fn)} returns ${left} - ${right} = ${left - right}`);
        return left - right;
    };
    try {
        // For browsers that don't do `name` properly
        fn.name = "fnFromMinus";
    } catch (e) { }
    show(`minus(${right}) returns ${fname(fn)}`);
    return fn;
}

console.log(eight(minus(three()))); // will log out 5
.as-console-wrapper {
    max-height: 100% !important;£
}


It's actually not that complicated if you work through it step by step. Let's work from the inside out. Calling:

three()

without a parameter means you're passing an undefined value as func. So it's the same as saying:

makeNum(3, undefined)

When makeNum sees that func is undefined, it returns num, which is 3.

So we've reduced this down to:

eight(minus(3))

Now let's see how

minus(3)

evaluates. minus sees that the parameter you've provided is 3, so it returns a function that can accept a "left" parameter and subtract the 3 that we've already provided from it. It does NOT execute. It just sits there, waiting to be called with a "left" parameter so it can subtract 3 from it.

So we've reduced this down to:

eight(function(left){
    return left - 3;
})

Now let's see how this evaluates. eight is called with the func parameter being this function:

function(left){
    return left - 3;
}

It then passes 8 and this function to makeNum when it says:

makeNum(8, func)

makeNum then sees that func has been provided (and is therefore not undefined) and returns func(num) back to the eight function, which is the same as passing:

8 - 3

back to the eight function. The eight function receives that value and then returns it. So we've reduced this down to:

5

which gets logged in the console.

Tags:

Javascript