Difference between "trampoline" methods - javascript

I've started learning JS recently, and this is my first dive into functional language territory.
Was doing the "trampoline" exercise in the functional-javascript-workshop npm module, and came across an interesting difference between my own solution and the official solution. Both work perfectly fine, but I'm at a loss as to what exactly is the practical difference between them. I understand my own solution pretty well, but don't exactly understand why the other one works too.
My Solution
function repeat(op, num) {
var _repeat = function() {
if (num <= 0) return;
op();
return repeat(op, --num);
};
return _repeat;
}
function trampoline(f) {
while (f instanceof Function) {
f = f();
}
return f;
}
module.exports = function(op, num) {
return trampoline(repeat(op, num));
};
Official Solution
function repeat(operation, num) {
return function() {
if (num <= 0) return
operation()
return repeat(operation, --num)
}
}
function trampoline(fn) {
while(fn && typeof fn === 'function') {
fn = fn()
}
}
module.exports = function(operation, num) {
trampoline(function() {
return repeat(operation, num)
})
}
Specifically, I'm curious about the last part - why does the official solution create an annonymous function instead of just passing repeat?

There's not really a reason.
Except maybe that the trampoline will make the all calls, while in your version the first invocation of repeat is done outside of it. This might be considered cleaner, and could make an actual difference when trampoline was more sophisticated (e.g. wrapping all execution in a try-catch).

Take a look at the trampoline:
function trampoline(fn) {
while(fn && typeof fn === 'function') {
fn = fn()
}
}
Notice how it keeps iterating as long asfn is a function, which it invokes.
So theoretically you can have as many nested functions and get the same result:
module.exports = function(operation, num) {
trampoline(function() {
return function() {
return function() {
return repeat(operation, num);
};
};
});
};
Demo: http://jsbin.com/yixaliyoye/1/edit
The Official solution has one more redundant step than your solution. No real reason for it, other than the original author probably thought it looked more readable.

Related

About javascript function valueOf\toString and 'curry' function behaved differently in Chrome,Firefox and node enviroment

I wrote a function:
function add(){
let arr = [];
arr = arr.concat(Array.prototype.slice.apply(arguments))
let fun = function(){
arr = arr.concat(Array.prototype.slice.apply(arguments))
return fun
}
fun.toString = function(){
console.log(222)
return arr.reduce(function(total, num){
return total+num
}, 0)
}
return fun
}
console.log(add(1,2)(2,3)(3))
This is in Chrome:
enter image description here
Two questions:
In first line, why is 'f 11' ,not '11'?
Why output 'f 11' firstly, not '222', I think the type conversion should execute firstly, and then output computed result on console.
Another strange thing, it is the result in Firefox with same codes:
enter image description here
And the result in node environment:
enter image description here
I do not understand why, it seems in FF and node, has not executed the computed operation.
Please help me...Thanks so much!
At first you can beautify the whole code a bit:
function add(..arr){
function fun(...args){
arr.push(...args);
return fun
}
fun.toString = function(){
return arr.reduce((total, num) => total + num)
};
return fun;
}
And as you noticed correctly, logging a function is completely up to the environment. Firefox and Node return the code of the function, while Chrome does sth like:
out( "f" + add.toString())
so our toString function gets called and something is logged. To have a consistent behaviour between the different environments we could call toString explicitly:
console.log(add(1)(2)(3).toString());
This can be inferred:
console.log("" + add(1)(2));
If what you want is add that is both variadic and curried (which I still think is weird), just do this:
const add = (...args) => {
let accum = args;
let f = (...fargs) => {
if (!fargs.length) {
return accum.reduce((a, b) => { return a + b; }, 0);
} else {
accum = accum.concat(fargs);
return f;
}
};
return f;
};
add(1,2,3)(); // 6
add(1)(2,3)(); // 6
add()(); // 0
Now you just call the returned function with no arguments to get the value out. You could toy with it to make it more performant (e.g. by using .push in a loop instead of .concat) but this should work.

Was javascript argument default?

Is there a way to tell the difference between this
((a=true)=>{return a})(true)
And this?
((a=true)=>{return a})()
They both return true - how can I tell whether that true is coming from the default value of the argument, or from the value passed in to that parameter?
Is it possible from outside the function?
Is it possible from inside the function?
Is there a way to tell the difference between these two?
No. Arrow function doesn't support argument object, so there is no way to check this.
But, if you are using non-arrow functions, you can get arguments number from inside the function. For example:
(function(a=true){return arguments.length;})(); // 0
(function(a=true){return arguments.length;})(true); // 1
You can easily figure out how to extend this to multiple arguments.
You cannot tell the difference in your specific examples, and there is no way to tell from outside the function definitely. The only exposed info is what you explicitly expose with your return value, which is just the boolean.
From inside the function, you can tell the difference if you rewrite your logic. You could change your function from
((a=true)=>{
return a;
})(true)
to
((...args)=>{
const a = args[0] === undefined ? true : args[0];
if (args.length > 0) console.log("passed arg");
else console.log("passed no arg");
return a;
})(true)
Note that you cannot combine this with default value syntax, so if you'd have to rewrite it to use rest syntax.
Alternatively, you could use a normal function instead of an arrow, and use arguments, however that is also a potentially difficult change if your real-world case relies on the arrow-function's lexical this. e.g.
(function(a = true)=>{
if (arguments.length > 0) console.log("passed arg");
else console.log("passed no arg");
return a;
})(true)
Although not ideal in every way, one solution might be:
(a=undefined) => {
let is_default = false;
if (a === undefined) {
is_default = true;
a = true;
}
return a
}
If you really want to use arrow functions, you can achieve a robust (if convoluted) result using Symbol and a wrapping IIFE
var fn = ((def) =>
(a = def) => {
if(a === def) {
console.log('defaulted');
a = true;
}
console.log('a is', a);
}
)(Symbol());
both fn() and fn(true) will result in a is true - however, fn() will also output defaulted and then set a = true - though this last step I guess doesn't have to done, depends on what the real content of the real function you really want to "detect" this in
Or, a better way as pointed out by #Bergi (I keep forgetting about the block scope in ES6+ :D )
var fn;
{
const def = Symbol();
fn = (a=def) => {
if(a === def) {
console.log('defaulted');
a = true;
}
console.log('a is', a);
};
}

Writing a unary function chainer, TypeError on codewars but no error on repl.it?

I worked on this problem on code wars for a little while and used repl.it to test it out. It's a simple unary function chainer, but it only works on repl.it, while codewars will give me a TypeError when given this code:
function chained(functions) {
var funcs = Array.prototype.slice.call(arguments);
return function (value){
var finalValue = funcs.reduce(function(prevVal, currFunc){
return currFunc(prevVal);
}, value);
return finalValue;
}
}
It tells me currFunc is not a function, but using the following test code I get the correct answers while running in repl.it:
function f1(x){ return x*2 }
function f2(x){ return x+2 }
function f3(x){ return Math.pow(x,2) }
console.log(chained(f1,f2,f3)(0));
Is there a reason why it isn't a function in codewars?
I had to look up the test on codewars. They give you this boilerplate ...
function chained(functions) {
//FIXME
}
Looking at the tests you can see that functions are being passed in an array ...
Test.assertEquals( chained([f1,f2,f3])(0), 4 )
Test.assertEquals( chained([f1,f2,f3])(2), 36 )
Test.assertEquals( chained([f3,f2,f1])(2), 12 )
The mistake you made is ...
var funcs = Array.prototype.slice.call(arguments);
... which would only work if chained was called like this ...
chained(f1,f2,f3)
Your code otherwise works and passes all tests on codewars. Here's the complete change ...
function chained(functions) {
var funcs = Array.prototype.slice.call(arguments);
return function (value){
var finalValue = funcsfunctions.reduce(function(prevVal, currFunc){
return currFunc(prevVal);
}, value);
return finalValue;
}
}
Lastly, here's my solution ^_^
const id = x => x;
const uncurry = f => (x,y) => f (x) (y);
const rcomp = f => g => x => g (f (x));
const chained = fs => fs.reduce(uncurry(rcomp), id);
While #naomik's solution is the correct answer and should be accepted, I just wanted to share an alternate solution using good old fashioned ES3:
function chained(functions) {
return function(x) {
var fs = functions, i = fs.length, y = x;
while (i > 0) y = fs[--i](y);
return y;
};
}
This is just to show that you don't really need to use reduce to write succinct code in this particular case. Furthermore, using a while loop is better for performance than using reduce. Finally, this code is also very understandable. You don't need to mentally juggle with reducing id and uncurry(rcomp) to understand how chaining can be implemented by folding functions.
The same idea just in ES6. Fun is an array of functions.
const chained = fun => {
return input => {
return fun.reduce((acc, currentFun) => currentFun(acc), input);
}
};
and in a little more difficult to read but beautiful modern JS version:
const chained = fun => input => fun.reduce((acc, currentFun) => currentFun(acc), input);

JS Function With Two Parentheses and Two Params

I'm trying to understand how a function works that is run with two parentheses and two parameters. Like so:
add(10)(10); // returns 20
I know how to write one that takes two params like so:
function add(a, b) {
return a + b;
}
add(10,10); // returns 20
How could I alter that function so it could be run with one set of parameters, or two, and produce the same result?
Any help is appreciated. Literally scratching my head over this.
Thanks in advance!
How could I alter that function so it could be run with one set of parameters, or two, and produce the same result?
You can almost do that, but I'm struggling to think of a good reason to.
Here's how: You detect how many arguments your function has received and, if it's received only one, you return a function instead of a number — and have that function add in the second number if it gets called:
function add(a,b) {
if (arguments.length === 1) {
return function(b2) { // You could call this arg `b` as well if you like,
return a + b2; // it would shadow (hide, supercede) the one above
};
}
return a + b;
}
console.log(add(10, 10)); // 20
console.log(add(10)(10)); // 20
I said "almost" above because just because the add function received only one argument, that doesn't guarantee that the caller is going to call the result. They could write:
var x = add(10);
...and never call the function that x now refers to.
Welcome to the wonderful world of first order functions
In JavaScript, a function can return a function since a function is just another object. A simple implementation is something like:
function add(x){
return function addOther(y){
return x + y;
};
}
This is possible because of closures and first order functions.
This also lets you do partial application, libraries like Ramda utilize this to great extent.
var addThree = add(3)
addThree(5); // 8
To extend what both T. J. Crowder and Benjamin Gruenbaum said, libraries like Ramda (disclosure: I'm one of the authors) allow you to convert a simple function like this:
function add(a, b) {
return a + b;
}
into the style under discussion by wrapping it in a call to a curry function:
var add = R.curry(function add(a, b) {
return a + b;
});
add(3, 5); //=> 8
add(3)(5); //=> 8
var add3 = add(3);
add3(5); //=> 8
The best article I know on this subject is Hugh Jackson's Why Curry Helps. I wrote a more detailed one at Favoring Curry.
Update
Here is a version of curry somewhat simpler than the one in Ramda. It would do the above and quite a bit more, but doesn't do some of the things that Ramda does with placeholder values:
// here is a function that takes a function and returns a curried version
// of it, that is, a version that performs the sort of partial application
// you describe.
var curry = function(fn) {
// first, we detect how many arguments the function has.
var fnArity = fn.length;
var partialApply = function(args) {
// now, let's create a function that's curried
return function () {
// collect the previous args as the partial, and add the new
// ones you just received
var newArgs = (args || []).concat([].slice.call(arguments, 0));
// if we have "enough" arguments, we don't need any more partial
// application and we can call the function.
if (newArgs.length >= fnArity) {
return fn.apply(this, newArgs);
} else { // else we return a partially applied version
return partialApply(newArgs);
}
};
};
return partialApply([]); // a function is itself partially applied with 0 args
};
function add() {
var sum = 0;
for (var i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
function total() {
for (var i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
return total;
}
total.toString = function () { return sum };
return total;
}
This will work for any no of arguments and parentheses.
https://medium.com/#imdebasispanda/super-function-with-closure-86a58a9a980b

More succinct delayed evaluation than function(){return x}?

I'm porting some Python code that relies heavily on delayed evaluation. This is accomplished by via thunks. More specifically, any Python expression <expr> for which delayed evaluation is desired gets enclosed within a Python "lambda expression", i.e. lambda:<expr>.
AFAIK, the closest JavaScript equivalent of this is function(){return <expr>}.
Since the code I'm working with is absolutely awash in such thunks, I'd like to make the code for them more succinct, if at all possible. The reason for this is not only to save characters (a non-negligible consideration when it comes to JS), but also to make the code more readable. To see what I mean, compare this standard JavaScript form:
function(){return fetchx()}
with
\fetchx()
In the first form, the substantive information, namely the expression fetchx(), is typographically obscured by the surrounding function(){return...}. In the second form1, just one (\) character is used as "delayed evaluation marker". I think this is the optimal approach2.
AFAICT, solutions to this problem would fall into the following categories:
Using eval to simulate delayed evaluation.
Some special JavaScript syntax that I don't know about, and that accomplishes what I want. (My vast ignorance of JavaScript makes this possibility look quite real to me.)
Writing the code in some non-standard JavaScript that gets programmatically processed into correct JavaScript. (Of course, this approach will not reduce the final code's footprint, but may at least retain some gains in readability.)
None of the above.
I'm particularly interested in hearing responses of the last three categories.
P.S.: I'm aware that the use of eval (option 1 above) is widely deprecated in the JS world, but, FWIW, below I give a toy illustration of this option.
The idea is to define a private wrapper class whose sole purpose would be to tag plain strings as JavaScript code for delayed evaluation. A factory method with a short name (e.g. C, for "CODE") is then used to reduce, e.g.,
function(){return fetchx()}
to
C('fetchx()')
First, definitions of the factory C and of the helper function maybe_eval:
var C = (function () {
function _delayed_eval(code) { this.code = code; }
_delayed_eval.prototype.val = function () { return eval(this.code) };
return function (code) { return new _delayed_eval(code) };
})();
var maybe_eval = (function () {
var _delayed_eval = C("").constructor;
return function (x) {
return x instanceof _delayed_eval ? x.val() : x;
}
})();
The following comparison between a get function and a lazyget function shows how the above would be used.
Both functions take three arguments: an object obj, a key key, and a default value, and they both should return obj[key] if key is present in obj, and otherwise, the default value.
The only difference between the two functions is that the default value for lazyget can be a thunk, and if so, it will get evaluated only if key is not in obj.
function get(obj, key, dflt) {
return obj.hasOwnProperty(key) ? obj[key] : dflt;
}
function lazyget(obj, key, lazydflt) {
return obj.hasOwnProperty(key) ? obj[key] : maybe_eval(lazydflt);
}
Too see these two functions in action, define:
function slow_foo() {
++slow_foo.times_called;
return "sorry for the wait!";
}
slow_foo.times_called = 0;
var someobj = {x: "quick!"};
Then, after evaluating the above, and using (e.g.) Firefox + Firebug, the following
console.log(slow_foo.times_called) // 0
console.log(get(someobj, "x", slow_foo())); // quick!
console.log(slow_foo.times_called) // 1
console.log(lazyget(someobj, "x",
C("slow_foo().toUpperCase()"))); // quick!
console.log(slow_foo.times_called) // 1
console.log(lazyget(someobj, "y",
C("slow_foo().toUpperCase()"))); // SORRY FOR THE WAIT!
console.log(slow_foo.times_called) // 2
console.log(lazyget(someobj, "y",
"slow_foo().toUpperCase()")); // slow_foo().toUpperCase()
console.log(slow_foo.times_called) // 2
prints out
0
quick!
1
quick!
1
SORRY FOR THE WAIT!
2
slow_foo().toUpperCase()
2
1...which may strike Haskell programmers as strangely familiar. :)
2There's another approach, the one used, e.g., by Mathematica, that avoids the need for delayed evaluation markers altogether. In this approach, as part of a function's definition, one can designate any one of its formal arguments for non-standard evaluation. Typographically, this approach is certainly maximally unobtrusive, but a bit too much so for my taste. Besides, it is not as flexible, IMHO, as using, e.g., \ as a delayed evaluation marker.
In my humble opinion I think you're looking at this problem from a wrong perspective. If you're creating thunks manually then you need to consider refactoring your code. In most cases thunks should be:
Either returned from lazy functions.
Or created by composing functions.
Returning Thunks from Lazy Functions
When I first started practicing functional programming in JavaScript I was mystified by the Y combinator. From what I had read online the Y combinator was a divine entity to be worshipped. It somehow allowed functions which didn't know their own name to call themselves. Hence it was the mathematical manifestation of recursion - one of the most important pillars of functional programming.
However understanding the Y combinator was no easy feat. Mike Vanier wrote that the knowledge of the Y combinator is a diving line between those people who are "functionally literate" and those who aren't. Honestly, the Y combinator in itself is dead simple to understand. However most articles online explain it backwards making it difficult to understand. For example Wikipedia defines the Y combinator as:
Y = λf.(λx.f (x x)) (λx.f (x x))
In JavaScript this would translate to:
function Y(f) {
return (function (x) {
return f(x(x));
}(function (x) {
return f(x(x));
}));
}
This definition of the Y combinator is unintuitive and it doesn't make apparent how the Y combinator is a manifestation of recursion. Not to mention that it cannot be used at all in eager languages like JavaScript because the expression x(x) is evaluated immediately resulting in an infinite loop which eventually results in a stack overflow. Hence in eager languages like JavaScript we use the Z combinator instead:
Z = λf.(λx.f (λv.((x x) v))) (λx.f (λv.((x x) v)))
The resulting code in JavaScript is even more confusing and unintuitive:
function Z(f) {
return (function (x) {
return f(function (v) {
return x(x)(v);
});
}(function (x) {
return f(function (v) {
return x(x)(v);
});
}));
}
Trivially we can see that the only difference between the Y combinator and the Z combinator is that the lazy expression x(x) is replaced by the eager expression function (v) { return x(x)(v); }. It is wrapped in a thunk. In JavaScript however it makes more sense to write the thunk as follows:
function () {
return x(x).apply(this, arguments);
}
Of course here we're assuming that x(x) evaluates to a function. In the case of the Y combinator this is indeed true. However if the thunk doesn't evaluate to a function then we simply return the expression.
One of the most epiphanous moments for me as a programmer was that the Y combinator is itself recursive. For example in Haskell you define Y combinator as follows:
y f = f (y f)
Because Haskell is a lazy language the y f in f (y f) is only evaluated when required and hence you don't run into an infinite loop. Internally Haskell creates a thunk for every expression. In JavaScript however you need to create a thunk explicitly:
function y(f) {
return function () {
return f(y(f)).apply(this, arguments);
};
}
Of course defining the Y combinator recursively is cheating: you are just explicitly recursing inside the Y combinator instead. Mathematically the Y combinator itself should be defined non-recursively to describe the structure of recursion. Nonetheless we all love it anyway. The important thing is that the Y combinator in JavaScript now returns a thunk (i.e. we defined it using lazy semantics).
To consolidate our understanding let's create another lazy function in JavaScript. Let's implement the repeat function from Haskell in JavaScript. In Haskell the repeat function is defined as follows:
repeat :: a -> [a]
repeat x = x : repeat x
As you can see repeat has no edge cases and it calls itself recursively. If Haskell weren't so lazy it would recurse forever. If JavaScript were lazy then we could implement repeat as follows:
function repeat(x) {
return [x, repeat(x)];
}
Unfortunately if executed the above code would recurse forever until it results in a stack overflow. To solve this problem we return a thunk instead:
function repeat(x) {
return function () {
return [x, repeat(x)];
};
}
Of course since the thunk doesn't evaluate to a function we need another way to treat a thunk and a normal value identically. Hence we create a function to evaluate a thunk as follows:
function evaluate(thunk) {
return typeof thunk === "function" ? thunk() : thunk;
}
The evaluate function can now be used to implement functions which can take either lazy or strict data structures as arguments. For example we can implement the take function from Haskell using evaluate. In Haskell take is defined as follows:
take :: Int -> [a] -> [a]
take 0 _ = []
take _ [] = []
take n (x:xs) = x : take (n - 1) xs
In JavaScript we would implement take using evaluate as follows:
function take(n, list) {
if (n) {
var xxs = evaluate(list);
return xxs.length ? [xxs[0], take(n - 1, xxs[1])] : [];
} else return [];
}
Now you can use repeat and take together as follows:
take(3, repeat('x'));
See the demo for yourself:
alert(JSON.stringify(take(3, repeat('x'))));
function take(n, list) {
if (n) {
var xxs = evaluate(list);
return xxs.length ? [xxs[0], take(n - 1, xxs[1])] : [];
} else return [];
}
function evaluate(thunk) {
return typeof thunk === "function" ? thunk() : thunk;
}
function repeat(x) {
return function () {
return [x, repeat(x)];
};
}
Lazy evaluation at work.
In my humble opinion most thunks should be those returned by lazy functions. You should never have to create a thunk manually. However every time you create a lazy function you still need to create a thunk inside it manually. This problem can be solved by lifting lazy functions as follows:
function lazy(f) {
return function () {
var g = f, self = this, args = arguments;
return function () {
var data = g.apply(self, args);
return typeof data === "function" ?
data.apply(this, arguments) : data;
};
};
}
Using the lazy function you can now define the Y combinator and repeat as follows:
var y = lazy(function (f) {
return f(y(f));
});
var repeat = lazy(function (x) {
return [x, repeat(x)];
});
This makes functional programming in JavaScript almost as fun as functional programming in Haskell or OCaml. See the updated demo:
var repeat = lazy(function (x) {
return [x, repeat(x)];
});
alert(JSON.stringify(take(3, repeat('x'))));
function take(n, list) {
if (n) {
var xxs = evaluate(list);
return xxs.length ? [xxs[0], take(n - 1, xxs[1])] : [];
} else return [];
}
function evaluate(thunk) {
return typeof thunk === "function" ? thunk() : thunk;
}
function lazy(f) {
return function () {
var g = f, self = this, args = arguments;
return function () {
var data = g.apply(self, args);
return typeof data === "function" ?
data.apply(this, arguments) : data;
};
};
}
Creating Thunks by Composing Functions
Sometimes you need to pass expressions to functions that are evaluated lazily. In such situations you need to create custom thunks. Hence we can't make use of the lazy function. In such cases you can use function composition as a viable alternative to manually creating thunks. Function composition is defined as follows in Haskell:
(.) :: (b -> c) -> (a -> b) -> a -> c
f . g = \x -> f (g x)
In JavaScript this translates to:
function compose(f, g) {
return function (x) {
return f(g(x));
};
}
However it makes much more sense to write it as:
function compose(f, g) {
return function () {
return f(g.apply(this, arguments));
};
}
Function composition in mathematics reads from right-to-left. However evaluation in JavaScript is always from left-to-right. For example in the expression slow_foo().toUpperCase() the function slow_foo is executed first and then the method toUpperCase is called on its return value. Hence we want to compose functions in reverse order and chain them as follows:
Function.prototype.pipe = function (f) {
var g = this;
return function () {
return f(g.apply(this, arguments));
};
};
Using the pipe method we can now compose functions as follows:
var toUpperCase = "".toUpperCase;
slow_foo.pipe(toUpperCase);
The above code will be equivalent to the following thunk:
function () {
return toUpperCase(slow_foo.apply(this, arguments));
}
However there's a problem. The toUpperCase function is actually a method. Hence the value returned by slow_foo should set the this pointer of toUpperCase. In short we want to pipe the output of slow_foo into toUpperCase as follows:
function () {
return slow_foo.apply(this, arguments).toUpperCase();
}
The solution is actually very simple and we don't need to modify our pipe method at all:
var bind = Function.bind;
var call = Function.call;
var bindable = bind.bind(bind); // bindable(f) === f.bind
var callable = bindable(call); // callable(f) === f.call
Using the callable method we can now refactor our code as follows:
var toUpperCase = "".toUpperCase;
slow_foo.pipe(callable(toUpperCase));
Since callable(toUpperCase) is equivalent to toUpperCase.call our thunk is now:
function () {
return toUpperCase.call(slow_foo.apply(this, arguments));
}
This is exactly what we want. Hence our final code is as follows:
var bind = Function.bind;
var call = Function.call;
var bindable = bind.bind(bind); // bindable(f) === f.bind
var callable = bindable(call); // callable(f) === f.call
var someobj = {x: "Quick."};
slow_foo.times_called = 0;
Function.prototype.pipe = function (f) {
var g = this;
return function () {
return f(g.apply(this, arguments));
};
};
function lazyget(obj, key, lazydflt) {
return obj.hasOwnProperty(key) ? obj[key] : evaluate(lazydflt);
}
function slow_foo() {
slow_foo.times_called++;
return "Sorry for keeping you waiting.";
}
function evaluate(thunk) {
return typeof thunk === "function" ? thunk() : thunk;
}
Then we define the test case:
console.log(slow_foo.times_called);
console.log(lazyget(someobj, "x", slow_foo()));
console.log(slow_foo.times_called);
console.log(lazyget(someobj, "x", slow_foo.pipe(callable("".toUpperCase))));
console.log(slow_foo.times_called);
console.log(lazyget(someobj, "y", slow_foo.pipe(callable("".toUpperCase))));
console.log(slow_foo.times_called);
console.log(lazyget(someobj, "y", "slow_foo().toUpperCase()"));
console.log(slow_foo.times_called);
And the result is as expected:
0
Quick.
1
Quick.
1
SORRY FOR KEEPING YOU WAITING.
2
slow_foo().toUpperCase()
2
Hence as you can see for most cases you never need to create thunks manually. Either lift functions using the function lazy to make them return thunks or compose functions to create new thunks.
If you want delayed execution you should look in to using setTimeout.
setTimeout(function() {
console.log("I'm delayed");
}, 10);
console.log("I'm not delayed");
>I'm not delayed
>I'm delayed
https://developer.mozilla.org/en-US/docs/Web/API/window.setTimeout

Categories