Understanding trampoline optimisation for recursive functions - javascript

I have got the following trampoline implementation:
function trampoline(f) {
while (f && f instanceof Function) {
f = f.apply(f.context, f.args);
}
return f;
}
And it works likewise, example being factorial:
function factorial(n) {
function recur(n, acc) {
if (n == 0) {
return acc;
} else {
return recur.bind(null, n-1, n*acc);
}
}
return trampoline(recur.bind(null, n, 1));
}
The problem is that I don't understand how f.context and f.args are passed in as arguments, those are properties that clearly can't be found on recurred function inside the trampoline when i, for instance, try to access and console.log them. It then logs undefined values.
What is the mechanism of this particular implementation that is passing arguments of the function?

As you can see here, function objects do not have properties called args and context, and that's why they appear as undefined in the console log. Inside the function itself, you can access these values in the arguments and this objects, but they are not visible from the outside in bound functions (or unbound functions).
The reason your code works is because the apply parameters are appended if the function is bound with the parameters. When you bind a function, you can bind only the context (this) or the context and the parameters. If you bind the context and the parameters, when you apply or call that function, the parameters you pass in will be appended at the end of the parameters list. In your particular case, you are doing...
f = f.apply(undefined, undefined);
... and the resulting parameters for f would be:
[...parametersWhenTheFunctionWasBound, undefined, undefined]

Related

What exactly does this do? [duplicate]

So I get that an array of [200,599] is returned from the promise and the callback function inside spread is being passed into Function.apply.bind, but now I'm lost. How is the array of [200,599] split into x and y? How exactly does the apply.bind work?
function getY(x) {
return new Promise( function(resolve,reject){
setTimeout( function(){
resolve( (3 * x) - 1 );
}, 100 );
} );
}
function foo(bar,baz) {
var x = bar * baz;
// return both promises
return [
Promise.resolve( x ),
getY( x )
];
}
function spread(fn) {
return Function.apply.bind( fn, null );
}
Promise.all(
foo( 10, 20 )
)
.then(
spread( function(x,y){
console.log( x, y ); // 200 599
} )
)
.apply() is a method on function objects. Like so:
console.log(Math.max.apply(null, [1, 2, 3])); // 3
.apply() accepts two arguments, the context (what would become this inside of the target function) and an iterable of arguments (usually an array, but the arguments array like also works).
.bind() is a method on function objects. Like so:
const x = {
foo: function() {
console.log(this.x);
},
x: 42
}
var myFoo = x.foo.bind({x: 5});
x.foo(); // 42
myFoo(); // 5
.bind() takes a context (what would become this), and optionally, additional arguments, and returns a new function, with the context bound, and the additional arguments locked
Since .apply() is a function in on itself, it can be bound with .bind(), like so:
Function.prototype.apply.bind(fn, null);
Meaning that the this of .apply() would be fn and the first argument to .apply() would be null. Meaning that it would look like this:
fn.apply(null, args)
Which would spread the parameters from an array.
Spread takes a function and binds the apply method to, partially applying the null argument. So in short,
spread(fn)
is transformed to
args => fn.apply(null, args)
which is the same as using the ES6 spread syntax
args => fn(...args)
where the function got its name from.
If you want the long answer, remember what bind does:
method.bind(context, ...args1)
returns a function that works like
(...args2) => method.call(context, ...args1, ...args2)
In our case, method is apply, the context is fn and the first arguments are null, so the call
Function.apply.bind( fn, null )
will create a function that works like
(...args2) => (Function.apply).call(fn, null, ...args2)
which is equivalent to the fn.apply(…) call above, given that apply is the method inherited from Function.prototype in both accesses.
The spread function is just a utility function to convert an array, into parameters passed to a function. The apply is doing the converting, and the bind is binding it to the calling function so that the "this" context is connected to same function.
To see how spread is working in a simpler form ->
spread(function (x,y){console.log(x,y);})([1,2])
You will get the answer, 1 2, as 1 is passed to x, and 2 is passed to y.
So in your example your promise.all is returning an array of resolved promises.
These are then getting mapped to parameters to your function(x,y).
The reason it works is the "destructuring" nature of apply (if given an array of values, they would be provided spreaded to the function you use apply on).
Now back to your code when calling bind on apply, the value returned is a function which returns the same function provided to bind, the only thing different is when executed it would be called using apply (with an array as thisArg in your case), but it isn't going to be executed until you call it.
In your case when the promise has resolved, the function provided tothen woule be executed with an array of arguments provided by Promise resolution.
function spread(fn){
let boundedFn = fn.bind(fn)
return function(arg){
return boundedFn.apply(null,arg)
}
}
spread((x, y, c) => console.log(x, y, c))([1,2,3])
// or
Promise.resolve([6, 5]).then(spread((a, b) => console.log(a, b)))
The reason bind is provided (in your code) with null as second param is to prevent the array provided by the caller from being given to apply as its first param, which reserved for this.

What is variable arg referring to in this function? Flow of arguments in Javascript

This is an answer to a SO question:
function typedFunction(paramsList, f){
//optionally, ensure that typedFunction is being called properly -- here's a start:
if (!(paramsList instanceof Array)) throw Error('invalid argument: paramsList must be an array');
//the type-checked function
return function(){
for(var i=0,p,arg;p=paramsList[i],arg=arguments[i],i<paramsList.length; i++){
if (typeof p === 'string'){
if (typeof arg !== p) throw new Error('expected type ' + p + ', got ' + typeof arg);
}
else { //function
if (!(arg instanceof p)) throw new Error('expected type ' + String(p).replace(/\s*\{.*/, '') + ', got ' + typeof arg);
}
}
//type checking passed; call the function itself
return f.apply(this, arguments);
};
}
//usage:
var ds = typedFunction([Date, 'string'], function(d, s){
console.log(d.toDateString(), s.substr(0));
});
ds('notadate', 'test');
//Error: expected type function Date(), got string
ds();
//Error: expected type function Date(), got undefined
ds(new Date(), 42);
//Error: expected type string, got number
ds(new Date(), 'success');
//Fri Jun 14 2013 success
It shows a generalized way of ensuring arguments conform to specified types. I am having a hard time grasping the flow of arguments from the ds function call, back to the function that is returned from typedFunction().
Here's what I expect:
paramsList should refer to the first parameter in typedFunction()'s function
arguments should refer to a list of all arguments passed to typedFunction, in this case both paramsList and f. However, it seems like arguments refers to the arguments passed in ds. That's where things get fuzzy. How do the arguments supplied in ds get passed to f?
Could you explain the logical flow of events/ how this code works?
Sorry if the question is not clear. Please edit the question if you have a grasp of what I'm trying to ask and can make the question easier to read.
For the sake of explanation, assume we name the function being returned as typeChecker:
function typedFunction(paramsList, f){
// ...
return function typeChecker() { ... }
}
Now, here's the rundown of what is happening:
Running
var ds = typedFunction([Date, 'string'], function(d, s){
console.log(d.toDateString(), s.substr(0));
});
binds [Date, 'string'] as paramsList and function(d, s) { ... } as f in the outer environment (closure) of typeChecker. This means typeChecker will, when called, have access to this data via the specified bound variable names paramsList and f.
Then we run:
ds(new Date(), 'success');
which essentially runs typeChecker(new Date(), 'success').
Side note: within any normal JS function (arrow function behaves differently), arguments is a special array-like object provided when the function is called. This object contains all the arguments passed into the function, irrelevant of how many parameters are specified in the function signature.
This means that, when we invoke ds(new Date(), 'success'), arguments conceptually looks like this:
[dateInstance, 'success']
although technically, it looks more like this:
{
0: dateInstance,
1: 'success'
}
but this makes no difference in the given code since both structures provide numeric property/index access being used in the for loop.
Therefore, inside typeChecker, arg will refer to the item at the corresponding index in the arguments object for each loop iteration, i.e. arg=arguments[i].
Once the iteration is finished, this runs:
return f.apply(this, arguments);
which calls the function initially given to typedFunction as its second parameter, the one that was bound to the name f in typeChecker's closure, supplying this as the this from within typeChecker and passing along arguments from typeChecker.

Functional Programming - .bind.apply for curry function

Reading about functional programming - got to currying, example has a simple currying function. I understand everything except the last else block.
var curry = function (fn, fnLength) {
fnLength = fnLength || fn.length;
return function () {
var suppliedArgs = Array.prototype.slice.call(arguments);
if (suppliedArgs.length >= fn.length) {
return fn.apply(this, suppliedArgs);
} else if (!suppliedArgs.length) {
return fn;
} else {
return curry(fn.bind.apply(fn, [this].concat(suppliedArgs)), fnLength - suppliedArgs.length);
}
};
};
If the supplied args are >=, call the function with the supplied arguments.
Else if suppliedArgs.length is falsy, return the original function without doing anything.
Else ???
I think recursively call the function?
I don't understand what .bind.apply accomplishes.
Is [this] just in an array because suppliedArgs.push wouldn't return the array?
Start by looking at how you call Function#bind():
fun.bind(thisArg[, arg1[, arg2[, ...]]])
Then consider how you use Function#apply():
fun.apply(thisArg, [argsArray])
So given for bind() we need to call it on a function, and give it multiple parameters (not an array), and all we have is an array of arguments (suppliedArgs in the code), then how can we do that? Well, the main way you can call a function that takes multiple arguments instead of a single argument that is an array is to use .apply() on the function. So then we have fn.bind.apply(...something...).
The first parameter to .apply() is the this value - which needs to be the function to be bound in the case of .bind() (see below for an explanation of why). Hence fn.bind.apply(fn, ...).
Then, the second parameter to .apply() is an array of all the arguments to the function you are invoking, which in the case of .bind() is thisArg[, arg1[, arg2[, ...]]]. Hence we need a single array, with the first value being the value for this within the function, followed by the other arguments. Which is what [this].concat(suppliedArgs) produces.
So the whole fn.apply.bind(fn, [this].concat(suppliedArgs)) thing produces a correctly bound function that will have the supplied arguments to the current function "prefilled", with the correct this context. This function that is produced is then passed as the fn parameter in a recursive call to curry(), which in turn will produce another function in the same way as the top level call will.
The overall effect is that whenever you call a function created by curry(), if you don't pass the expected number of parameters, you will get a new function which takes the remaining number of parameters, or you will evaluate the original function with the entire list of parameters passed in correctly.
e.g.
function addAllNums(a, b, c, d, e) {
return a + b + c + d + e;
}
var curriedAddAll = curry(addAllNums, 5);
var f1 = curriedAddAll(1); // produces a function expecting 4 arguments
var f2 = f1(2, 3); // f2 is a function that expects 2 arguments
var f3 = f2(4); // f3 is a function that expects 1 argument
var f4 = f3(5); // f4 doesn't expect any arguments
var ans = f4(); // ans = 1 + 2 + 3 + 4 + 5 = 15.
// OR var ans = f3(5); => same result
Why the different thisArg values?
Probably the most confusing thing about this line of code is the two different values for thisArg in .bind() and .apply().
For .apply(), the thisArg is what you want the value of this to be inside the function you are calling .apply() on. e.g. myFunction.apply(myObj, ['param1', 'param2']) is equivalent to myObj.myFunction('param1', 'param2').
In this particular case, .bind() is executed on the fn function, so we want fn to be the this value for .bind(), so it knows what function it is creating a bound version of.
For .bind(), the thisArg is what the value of this will be inside the bound function that is returned.
In our case, we want to return a bound function that has the same this value as we currently have. In other words, we want to maintain the this value correctly within the new function, so it doesn't get lost as you create new functions which happens when you call a curried function with less arguments than it expects.
If we did not maintain the this value correctly, the following example wouldn't log the correct value of this. But by maintaining it, the correct value will be output.
var myObj = {
a: 1,
b: curry(function (a, b, c, d) {
console.log('this = ', this);
return a + b + c + d;
})
};
var c = myObj.b(1,1,1); // c is a function expecting 1 argument
c(1); // returns 4, and correctly logs "this = Object {a: 1, b: function}"
// if "this" wasn't maintained, it would log the value of "this" as
// the global window object.
The last else block is the main and most important part of the curry function, as it is the actual line that carries the logic for currying.
return curry(fn.bind.apply(fn, [this].concat(suppliedArgs)), fnLength - suppliedArgs.length);
This is what returns the new function that needs n-1 arguments from your previous function. Why? It's a combination of multiple things:
fn.bind.apply simply calls a function in the context of the function itself, while supplying the needed args (suppliedArgs). Note how the next parameter to curry is fnLength - suppliedArgs.length, which reduces the arguments needed to what was passed.
Let's explain it with the help of ES6. Things are going to become more obvious.
// Imagine we have the following code written in ES5
function fn(a, b, c) {
console.log(a, b, c);
}
var arr = [1, 2, 3];
var funcWithBoundArguments = fn.bind.apply(fn, [null].concat(arr));
Let's convert ES5 to ES6 code
// ES6
function fn(a, b, c) { console.log(a, b, c) }
let arr = [1,2,3];
let funcWithBoundArguments = fn.bind(null, ...arr)
You see? When you bind a function we have to explicitly enumerate all the arguments like:
fn.bind(null, 1, 2, 3)
But how could we bind the content of an array if we don't know its content in advance?
Right, we have to use .bind.apply() where:
the 1st argument of apply is the function (fn) we bind
the 2nd argument is an array which gets the context (as the first item of array) that we bind our function to and the rest of the items of the array are the arguments (which number is variable) we bind to our function (fn).

Not understanding function and how it is called from Functional Javascript

I just started reading Functional JavaScript and immediately was introduced to a function that I don't understand:
function splat(fun) {
return function(array) {
return fun.apply(null, array);
};
}
var addArrayElements = splat(function(x, y) { return x + y });
addArrayElements([1, 2]);
//=> 3
How does splat(function(x, y) { return x + y }) work. It's called with the array [1,2], but it seems like the anonymous function inside the call to splat takes two parameters, not one array.
Putting console.log(fun) on line 2 of this code shows that fun is the entirety of the anonymous function(x, y) { return x + y }. console.log(array) after return function(array) { shows that array is [1, 2]. Where does array come from then?
Thanks much.
It might be simpler to see how this function would have been written without using the .apply method:
function splat(fun) {
return function(array) {
return fun(array[0], array[1]);
};
}
First you call splat, passing it a function:
var add = function(x,y){ return x + 1 };
var ff = splat(add);
At this point, ff refers to the function(array) function, meaning its an one-argument function. The private variable fun refers to the add function.
Now, you call ff passing its one argument
ff([1,2]);
and it uses the values in the array to call fun with two arguments
return fun(array[0], array[1]);
The only difference between this and the real example is that the apply method lets you work with any argument array length instead of hardcoding a specific length (2) like I did.
//Every time we call this function, we get another one back
function splat(fun) {
return function(array) { // <-- this one will be returned in splat();
return fun.apply(null, array);
};
}
//Step one, call splat, pass a function as parameter
var addArrayElements = splat(function(x, y) { return x + y });
/*
Get back a function that accepts an array, and will execute the function we just passed in on it
*/
// This will call the newly created function, func will be available because it's in a closure
addArrayElements([1, 2]);
The last thing is that, even if the anonymous function takes two parameters, we call apply on it so it will bind array[0] ==> x and array[1] ==> y
This is an example of a higher order function. That's a function that takes functions as arguments and returns functions instead of just regular values (though functions are "just regular values" in Javascript). In this case:
function splat(fun) {
splat takes a function as its argument...
return function(array) {
...and returns a new function which takes an array...
return fun.apply(null, array);
...and when called calls the first fun function with the array .applied as its arguments.
So splat takes one function which expects several parameters and wraps it in a function which takes an array of parameters instead. The name "splat" comes from languages like Ruby, where a * (a "splat" or "squashed bug") in the parameter list of a function accumulates an arbitrary number of arguments into an array.
var addArrayElements = splat(function(x, y) { return x + y });
addArrayElements is now basically:
function (array) {
// closed over variable:
// var fun = function(x, y) { return x + y }
return fun.apply(null, array);
}
Here this is realized by a closure, which closes over and "preserves" the original fun passed to splat in the new returned function.
addArrayElements = function(array) { fun.apply(null, array); };
BUT
it has a closure whereby the variable context of its containing scope (that of the splat function that created the anonymous function) remains visible and accessible.
In JavaScript, functions are first-class objects that can be referenced and passed as arguments or, as in this case, through the closure mechanism.
Edit: about JavaScript and scope
In most languages, variables are, by default, local to the scope they're defined in (which usually is a function's local symbol table). By contrast, in JavaScript a variable is local only if it is defined using the var keyword; otherwise, the symbol will be looked back in the chain of the containing scopes, up to the implicit root object (which in the case of web browsers is window. I.e.,
function foo() { someVar = "bar"; }
foo();
alert(someVar); // shows "bar"
Not being restricted to the local scope, the symbol has been (purposely or not) leaked to the root scope.
Taking it one step further:
function foo() {
var baz = function() {
someVar = "bar";
};
baz();
}
foo();
alert(someVar); // shows "bar"
However, if you declare someVar within foo:
function foo() {
var someVar;
var baz = function() {
someVar = "bar";
};
baz();
alert("someVar in foo=" + someVar); // shows "bar"
}
foo();
alert("someVar in root=" + window.someVar); // shows "undefined"
Note that in this last version I needed to use window.someVar instead of just someVar because someVar never got defined as a variable in the root scope nor as a property of the root object, which caused an error.
a more functional approach uses bind(), which is short enough you don't really need splat anymore, and it's always nice to eliminate closures:
var addArrayElements = Function.apply.bind( function(x, y) { return x + y } , null );
addArrayElements([1, 2]); // === 3

Javascript: Forwarding function calls that take variable number of arguments [duplicate]

This question already has answers here:
Passing arguments forward to another javascript function
(5 answers)
Closed 5 years ago.
I think I need something like ruby's splat * here.
function foo() {
var result = '';
for (var i = 0; i < arguments.length; i++) {
result += arguments[i];
}
return result;
}
function bar() {
return foo(arguments) // this line doesn't work as I expect
}
bar(1, 2, 3);
I want this to return "123", but instead I get "[object Arguments]". Which makes sense, I suppose. It's passing the object that represents the arguments, but not the arguments individually.
So how do I simply forward any number of arguments to another function that takes any number of arguments?
UPDATE: Since ES6, you can use the spread syntax to call a function, applying the elements of an iterable object as argument values of the function call:
function bar() {
return foo(...arguments);
}
Note that you can also receive a variable number of arguments as a real array, instead of using the arguments object.
For example:
function sum(...args) { // args is an array
return args.reduce((total, num) => total + num)
}
function bar(...args) {
return sum(...args) // this just forwards the call spreading the argument values
}
console.log(bar(1, 2, 3)); // 6
In the days of ES3/ES5, to correctly pass the arguments to another function, you needed to use apply:
function bar() {
return foo.apply(null, arguments);
}
The apply method takes two parameters, the first is the thisObj, the value of it will be used as the this value inside the invoked function, if you use null or undefined, the this value inside the function will refer to the global object, in non-strict mode, otherwise is undefined.
The second argument that apply expects is an array-like object that contains the argument values to be applied to the function.
Check the above example here.
Try this return foo.apply(this,arguments). Also you can just use Array.prototype.slice.apply(arguments).join('') for your foo function.

Categories

Resources