1

n the function below, a comparison is made (finding out) which function called it. if the one is the most nested (internal), then a number is returned, and if the most external, then the function is executed.

var n = function(digit) {
  return function(op) {
    return op ? op(digit) : digit;
  }
};

these are variables to which function calls are assigned.

var zero = n(0);
var one = n(1);
var two = n(2);
var three = n(3);
var four = n(4);
var five = n(5);
var six = n(6);
var seven = n(7);
var eight = n(8);
var nine = n(9);

and this is the "operator" function

function plus(r) { return function(l) { return l + r; }; }
function minus(r) { return function(l) { return l - r; }; }
function times(r) { return function(l) { return l * r; }; }
function dividedBy(r) { return function(l) { return l / r; }; }

and this is the performance of functions, for the sake of which everything was written

one(plus(two())), 3);
seven(times(five())), 35);
four(plus(nine())), 13);
eight(minus(three())), 5);

that is, I understand how the code works in general, but I don’t understand the details, and I’m not able to consciously write such code myself because I don’t understand how it works in detail. This is where the problem is. thanks in advance

HakunaDio
  • 11
  • 1

1 Answers1

0

The functions returned by n and plus, etc., are closures over the paramter value n, etc., were called with. For instance, with n:

var n = function(digit) {
  return function(op) {
    return op ? op(digit) : digit;
  }
};
// ...
var one = n(1);
var two = n(2);

So in the case of two, the function n returned (assigned to two) closes over the value of digit for the call (2), so it has access to it later.

Code calling two can do one of two things:

  1. Call it with no arguments: two()
  2. Call it with a function: two(plus(one()))

If two is called with no arguments, op will be undefined, so this:

return op ? op(digit) : digit;

becomes effectively

return digit;

So that means, two() returns 2.

But if you pass a function into two, it calls that function with 2 as an argument. That's where things like plus come into it:

function plus(r) { return function(l) { return l + r; }; }

plus returns a function that closes over r. That function, when you call it with a value (l), returns the result of l + r.

So let's look at that in action: two(plus(one())). Evaluation is inside out, so:

  1. one() is called with no arguments, so it returns 1; now we effectively have:
    two(plus(1))
    
  2. plus() is called with 1 and returns function(l) { return l + r; } which is effectively function(l) { return l + 1; } (because r is 1); so now we effectively have:
    two(function(l) { return l + 1; })
    
  3. two is called with the function from Step 2 as op. Since it was given a function, two calls op(2) and returns its result.
  4. op (the function from Step 2) returns 2 + 1
  5. The result is 3.

Here's a version that makes it easier to see what's going on:

const n = function(digit) {
    console.log(`n:       creating "digitFn" for ${digit}`);
    const digitFn = function(op) {
        if (op) {
            console.log(`digitFn: evaling op(${digit})`);
            const result = op(digit);
            console.log(`digitFn: returning ${result}`);
            return result;
        }
        console.log(`digitFn: no "op", returning ${digit}`);
        return digit;
    };
    return digitFn;
};
const plus = function(r) {
    console.log(`plus:    creating "opFn" for + ${r}`);
    const opFn = function(l) {
        console.log(`opFn:    evaling ${l} + ${r}`);
        const result = l + r;
        console.log(`opFn:    returning ${result}`);
        return result;
    };
    return opFn;
};

console.log("main:    Creating `one` and `two`");
const one = n(1);
const two = n(2);

console.log("main:    Doing `two(plus(one()))`");
console.log(two(plus(one())));
.as-console-wrapper {
    max-height: 100% !important;
}

This overall thing is called partial application: Taking a function that takes N arguments, "baking in" one or more of those arguments to produce a function that takes < N arguments (because some are already provided).


Side note: Because you'll see this in the wild, I'll just note that you'll probably see these written as concise-form arrow functions, which are written like this: (params) => expression (if there's only one parameter, the () are optional: param => expression). What makes those concise-form arrow functions is the fact that the first thing after the arrow (=>) is not a { (more here). When called, a concise arrow function evaluates the expression and returns the result (implicitly, there's no return keyword in a concise arrow function). Those can seem complicated and intimidating at first, until you get used to reading them. Here's n:

// Does exactly what your original `n` does
const n = digit => op => op ? op(digit) : digit;

That breaks down like this:

  • digit => ____ is n, which returns the result of the expression ____
  • op => ____ is the function n returns
  • op ? op(digit) : digit is the expression the function created by n evaluates

Here's the full list:

const n = digit => op => op ? op(digit) : digit;

const zero = n(0);
const one = n(1);
const two = n(2);
const three = n(3);
const four = n(4);
const five = n(5);
const six = n(6);
const seven = n(7);
const eight = n(8);
const nine = n(9);

// Do exactly the same thing your original functions do
const plus      = r => l => l + r;
const minus     = r => l => l - r;
const times     = r => l => l * r;
const dividedBy = r => l => l / r;
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875