4

I am reading the book "Eloquent JavaScript".

In Chapter 5 he describes a particular higher-order function. It is called noisy() it is printed below...

function noisy(f) {
  return (...args) => {
   console.log("calling with", args);
   let result = f(...args);
   console.log("called with", args, ", returned", result);
   return result;
 };
}

Here is the part that is confusing me. He calls the function noisy as follows...

noisy(Math.min)(3,2,1);

I do not understand why the function is called that way. Why isn't it called like this...

noisy(Math.Min(3,2,1))

Edit: I see now what is going on. It was explained by Simone below.

noisy(Math.min)(3,2,1) is equivalent to (noisy(Math.min))(3,2,1).
Simone
  • 18,870
  • 12
  • 73
  • 98
root44
  • 65
  • 4
  • `noisy` accepts a function and returns a function which calls the passed function. – pishpish Apr 24 '18 at 14:12
  • I assume the purpose of `noisy` is to make some (well...) noise upon invoking the function (`f`) that is being passed to it. So, after you call `noisy(Math.min)` you get back a function that is then invoked using the `(3,2,1)` parameters. Essentially, decorating (or "proxying") the call to `f` for the purpose of logging to console before and after the actual invocation of `f`. – haim770 Apr 24 '18 at 14:12
  • 1
    Possible duplicate of [What is 'Currying'?](https://stackoverflow.com/questions/36314/what-is-currying) – Etheryte Apr 24 '18 at 14:13

4 Answers4

2

If you try to get the type of noisy, you'll get:

typeof noisy
>> "function"

Same thing if you ask for the type of noisy(Math.min):

typeof noisy(Math.min)
>> "function"

If you want you can also store this function into a variable:

const noisyFunction = noisy(Math.min)

So that you can call it like a regular function:

noisyFunction(1,2,3)

noisy(Math.min)(3,2,1) is exactly the same, just written in a different, shorter way. The main point is, a higher-order function is just a function that returns a function.

Simone
  • 18,870
  • 12
  • 73
  • 98
  • Ok I get it. It could have been written like this (noisy(Math.min))(3,2,1) The precedence of the () operator causes it to be executed like I have written? – root44 Apr 24 '18 at 14:36
  • Yes, correct, but it won't work if written like in your second example, because they're not equivalent. `noisy(Math.min(3,2,1))` is a regular function call where `Math.min(3,2,1)` is the first and only argument. – Simone Apr 24 '18 at 14:43
  • Yes I understand that now. Thanks. – root44 Apr 24 '18 at 16:04
  • Thanks so much for posting this. I had the exact same question! – Mick_eh Oct 22 '20 at 14:05
0

noisy takes a function as a parameter, and returns a function. The thing you pass it is expected to be a function. We can see that in this line here:

let result = f(...args);

f is the parameter. That line of code using it as a function.

noisy gives you a function back. We can see that here in the code:

return (...args) => {

The important thing is that a function is not the same as the value returned by a call to the function. Math.min is a function, a thing. Math.min(1, 2, 3) is a call to a function, an action -- which returns a number. When you put the parentheses on Math.min, that tells the compiler you want to execute Math.min.

Math.min(1, 2, 3) doesn't return a function, it returns a number. The code you were given isn't passing that parameter list to Math.min; it's passing that parameter list to a completely different function, the one returned by noisy.

This becomes more clear if we assign the values to local variables:

//  x is a function, not a number. 
var x = Math.min;

//  y is a function: noisy returns a function.
var y = noisy(x);

//  Call the function noisy returned:
var z = y(1, 2, 3);

If we break down your version of the call, noisy(Math.Min(3,2,1)), we get this:

//  Set x equal to the integer value 1
var x = Math.min(3, 2, 1);
var y = noisy(x);

Look inside noisy. It returns a function which includes the following line:

let result = f(...args);

So, in your version, y is the function that noisy returns. It's never executed, but if it were, it would be trying to treat the integer value 1 as a function. But an integer is not a function.

0

noisy(Math.min) returns a function (see the return statement: return (...args) => { ... }).

noisy(Math.min)(3,2,1); just immediately calls that function.

You could also first assign the function to a variable and then call it like this:

let fnct = noisy(Math.min);
fnct(3,2,1);

You cannot call it like noisy(Math.Min(3,2,1)) as Math.min(3,2,1) returns a number but noisy expects a function reference to be passed (hence Math.min - note its missing the () as it's passing a reference to that function rather than the result of its execution).

Your call would break in line 4, i.e:

let result = f(...args);

as f is not a function in your case but rather the result of Math.min(3,2,1) // = 1 .

Kristianmitk
  • 4,160
  • 4
  • 23
  • 41
0

noisy returns a function that uses the parameter f as a callback function.

Here is an es5 version of the code (which needs a little more code to maintain functionality) to help you understand:

function noisy(f) {
    return function () {
        var args = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            args[_i] = arguments[_i];
        }
        console.log("calling with", args);
        var result = f.apply(void 0, args);
        console.log("called with", args, ", returned", result);
        return result;
    };
}
//test
console.log(noisy(Math.min)(3, 2, 1));
Emil S. Jørgensen
  • 6,020
  • 1
  • 11
  • 25