Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Need help understanding function invocation in JavaScript

Tags:

javascript

I am having a hard time understanding a bit of example code from the book JavaScript Allongé (free in the online version).

The example code is a function for calculating the circumference for a given diameter. It shows different ways to bind values with names. One way to go about it, according to the book is this:

(
 (diameter) =>
  ((PI) => diameter * PI)(3.14159265)
)(2);
// calculates circumference given diameter 2

It further states:

Well, the wrinkle with this is that typically, invoking functions is considerably more expensive than evaluating expressions. Every time we invoke the outer function, we’ll invoke the inner function. We could get around this by writing

(
 ((PI) =>
   (diameter) => diameter * PI
 )(3.14159265)
)(2);

I cannot understand how it gets around the situation with calling two functions, aren't there exactly two function calls in both the examples? How do they differ from each other?

like image 576
userid1765 Avatar asked Aug 07 '16 11:08

userid1765


1 Answers

This probably looks a bit confusing because I don't think it's explained very well. Or, rather, I don't think it's explained in a typical JavaScript way.

Let's break down the examples

First Example

Breakdown

var calculateCircumference = (diameter) => (
    (PI) => diameter * PI)(3.14159265)
);

calculateCircumference(2); // 6.2831853

Arranged like this, here is what happens if you call this code

  1. You pass the diameter (e.g., 2)
  2. A new function is created that takes PI as parameter and uses it to calculate the circumference. This function is immediately invoked
  3. The function uses both variables present to do the calculation

Aside from being wasteful computation-wise (two invocation) this example is also convoluted for no good reason. The inner function is pointless and doesn't gain you anything. It's probably where the example loses lots of its clarity - seems like the only reason to have the example work as it is, is to set up for the second example.

Second Example

On currying

Before tackling the example, it seems like the book probably failed to mention how exactly it works. The second example leverages a technique called curry which is used in functional programming - it is not specific to JavaScript but it is still widely known as that name in the JavaScript world. A very brief overview of currying

//non-curried
function add(a, b) { // or, in ES6: (a, b) => a + b;
    return a + b;
}

//curried
function curryAdd(a) { //or in ES6: (a) => (b) => a + b;
    return function(b) {
        return a + b;
    }
}

//invocation
add(2, 3); // 5
curryAdd(2)(3); // 5

I will not go into detail but essentially, a curried function that takes multiple parameters, can be passed less and it will return a new function that can take the rest. When all the parameters are satisfied, you will get the result - in a formal notation, the curryAdd function will be expressed as curryAdd :: Number -> Number -> Number - it's a function that takes a number and returns another function that takes a number which finally returns another number. For why you would want to do that, here is an example - it's trivial but it gets the point accross:

//add5:: Number -> Number
add5 = curryAdd(5);

add5(3); // 8
add5(10); // 15
[1, 2, 3].map(add5); // [6, 7, 8]

Currying is a bit like partial allocation of functions but the two are not (necessarily) the same thing.

Breakdown

With that said, let's look at the second example:

//curryMultiply :: Float -> Float -> Float
(PI) => (diameter) => diameter * PI
//another way to write it:
//(a) => (b) => a * b

Hopefully that clarifies what is going on a bit. I'll re-write the rest of the example into what is actually happening:

// calculateCircumference :: Float -> Float
var calculateCircumference = curryMultiply(3.14159265);

calculateCircumference(2); //6.2831853

The second example's code is equivalent to the above. It avoids invoking a function twice because the outer function (which I dubbed curryMultiply) is invoked only once - any time you call the calculateCircumference function, you are only evaluating the inner function.

like image 176
VLAZ Avatar answered Oct 13 '22 09:10

VLAZ