mocking a function inside a function and getting calls count in jest - javascript

Considering this module in script.js:
const randomizeRange = (min, max) => {
return Math.floor(Math.random() * (max - min) + min);
};
const randomArr = (n, min, max) => {
const arr = [];
for (let i = 0; i < n; i++) {
arr.push(randomizeRange(min, max));
}
return arr;
};
module.exports = { randomArr, randomizeRange };
In the test file below, shouldn't the call count of randomizeRangeMock be 100 because it is being called inside randomArrMock which is called once? Why do I get 0
const randomArr = require("./script").randomArr;
const randomizeRange = require("./script").randomizeRange;
const randomArrMock = jest.fn(randomArr);
const randomizeRangeMock = jest.fn(randomizeRange);
test("tests for randomArr", () => {
expect(randomArrMock(100, 20, 1000)).toHaveLength(100);
expect(randomArrMock.mock.calls.length).toBe(1)
expect(randomizeRangeMock.mock.calls.length).toBe(100) //This fails because its 0
});

I appreciate that you are trying to just understand jest with this question and so I will answer as best I can. I'd normally recommend that you don't mock functions unless you are making some kind of complex back end call to a server or database or whatever. The more real testing you can do with real code (and not mocks) is infinitely more beneficial. That would be my tip for you going forward.
But for the purpose of this question - here's what's going on.
The reason you're not seeing your mocks work in the right way here is because you've essentially created two separate mocks:
You've created a single mock for randomizeArr.
You've created another single, separate mock for randomizeRange.
When you invoke randomArrMock(100, 20, 1000) in your test, this will invoke your single mock for randomizeArr as you've designated but that mock has absolutely no concept of the other mock for randomizeRange ever existing. And so it's never invoked behind the scenes. So this is why you see a 0 for the amount of calls at the end.
It's just a matter of changing your setup a little bit here to designate which mocks are invoked and when in order to see both work together as you're expecting.
What I would actually choose here is not to mock your randomizeRange method at all. That method invoked Math.floor and that's going to be much easier to "spy on" (which is a hint for the method we're going to use here) in this case. That Math.floor method is invoked once with each call to randomizeRange and so it will be an ideal choice. It should also be called 100 times, the same as your method.
The reason we're choosing to spyOn the method rather than to use an actual mock in the way you have done already is because we want to keep the original behaviour and so spying on how many times the method is called without overwriting the intended behaviour is ideal.
So keep your existing mock for randomizeArr as follows:
const randomArrMock = jest.fn(randomArr);
Now set up the spy for Math.floor
const mathFloorSpy = jest.spyOn(Math.prototype, `floor`);
This "spy" will effectively spy on this method without overwriting its behaviour. You can overwrite the behaviour if you want to but we want to keep the behaviour intact while just counting how many times it is called.
So now when you run your test suite:
test("tests for randomArr", () => {
expect(randomArrMock(100, 20, 1000)).toHaveLength(100);
expect(randomArrMock.mock.calls.length).toBe(1)
expect(mathFloorSpy.mock.calls.length).toBe(100)
});
This will now pass as Math.floor will have been invoked each time your randomizeRange method was called.
Finally don't forget to restore the original Math.floor functionality when your test is finished by using:
mathFloorSpy.mockRestore();
Happy testing!

This seems to be a common question, and the most "official" solution I can find was on the Jest GitHub issues page, at https://github.com/facebook/jest/issues/936#issuecomment-545080082.
Since you are using require instead of ES modules transpiled with babel, the solutions described in that linked issue don't seem to apply as well.
I would modify your script.js module to directly reference the exports used, so that the mocks refer to the correct function:
exports.randomizeRange = (min, max) => {
return Math.floor(Math.random() * (max - min) + min);
};
exports.randomArr = (n, min, max) => {
const arr = [];
for (let i = 0; i < n; i++) {
arr.push(exports.randomizeRange(min, max));
}
return arr;
};
And then your test implementation would reference the mocked functions:
const myScript = require('../script');
jest.spyOn(myScript, 'randomArr');
jest.spyOn(myScript, 'randomizeRange');
test("tests for randomArr", () => {
expect(myScript.randomArr(100, 20, 1000)).toHaveLength(100);
expect(myScript.randomArr).toHaveBeenCalledTimes(1);
expect(myScript.randomizeRange).toHaveBeenCalledTimes(100);
});
Using spyOn and toHaveBeenCalledTimes improves readability here.
Now, giving me own piece of advice: Write testable code. If your code is testable, well, you can easily write tests for it, and testable code is generally more modular and flexible; but don't focus on testing implementation details.
In your example, if you want to expose a randomArr method, then don't also expose randomizeRange; your randomArr method can call randomizeRange, but that is an implementation detail you shouldn't worry about.
If you want to make your randomArr much easier to test, consider making the range method (or the range) a parameter. You can then test it without testing some implementation of how it generates random ranges. Something like this:
exports.randomArr = (n, min, max, randomRangeGenerator) => {
const arr = [];
for (let i = 0; i < n; i++) {
arr.push(randomRangeGenerator(min, max));
}
return arr;
};
You can extend this pattern to randomizeRange as well. For testing that method, either pass in the floor and random functions as parameters, or mock them (through the Math object) in your tests. Working with Math.random in tests is difficult since it will produce different values on each run, so it's important mock that.

Related

Calling a variable from inside another function is bad?

I want to make my JS code to be less repetitive with an organized look. But I don't know if calling a function from inside another function is a bad practice, like Global Variables.
I share a piece of the code here.
thanks.
function getEx() {
return document.getElementById('example')
}
function getExTwo() {
return document.getElementById("exampleTwo");
}
function getTheValue() {
let getExValue = getEx();
let getExTwoValue = getExTwo();
}
Calling a function from within another function is absolutely not bad coding. That's part of what functions are for, really -- breaking up logical processes into smaller pieces.
Here's an example of how this can work.
// Note: This is new ES6/ES7 syntax for writing JavaScript functions.
// I'm using it here because it's very terse.
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
const square = (a) => multiply(a, a);
const sumOfSquares = (arr) => {
let sum = 0;
arr.forEach(number => sum += square(number));
return sum;
};
In the (simplified) example above, we use different functions to break up the distinct logical pieces of the problem into smaller, more manageable problems. For example, to calculate the sum of the squares of the array [1, 10, 12], we want to be able to add things and we want to be able to square things, so it's a good idea to create functions for performing each of those steps. We might even want to use other functions within those functions (e.g. calling multiply from within square).
Now, is it possible to go overboard with creating new functions? Yes. Try to avoid writing multiple functions that are basically the same. But otherwise... go nuts!
Calling a function from within another function is not bad. It is a recommended way of reducing repetition by breaking your code into smaller pieces, each handling some specific logic.
Here is a simplified version of your code:
// ps: $ is not from jquery it is just a normal variable.
const $ = document.querySelector
const getValues = () => {
const firstVal = $('#example')
const secondVal = $('#exampleTwo')
}

Batching functions synchronously?

This is a tricky problem. Is it possible to batch function calls without waiting for the next tick of the event loop?
Derived example for simplicity.
let numbers = []
let total = 0
const addNumber = (num) => {
numbers.push(num)
compute()
}
// for the sake of this argument let's say compute is very expensive to run
const compute = () => {
numbers.forEach((num) => {
total += num
})
numbers = []
}
This is currently what happens. compute runs after every addNumber because we need the total to be computed synchronously.
This is the example we need to work. We can only call addNumber we cannot directly call compute. Is there a way change addNumber so this example works but only calls compute once?
addNumber(2)
addNumber(4)
addNumber(10)
console.log(total === 16) // needs to be true
Thanks for any help!
No. What I'd suggest is move the placement of your compute function so it calculates the value of total just before you need to retrieve it.
Any other option I can think of would entail some kind of asynchronous functionality, which you've said you don't want.

Why is using a generator function slower than filling and iterating an array in this example?

A Tale of Two Functions
I have one function that fills an array up to a specified value:
function getNumberArray(maxValue) {
const a = [];
for (let i = 0; i < maxValue; i++) {
a.push(i);
}
return a;
}
And a similar generator function that instead yields each value:
function* getNumberGenerator(maxValue) {
for (let i = 0; i < maxValue; i++) {
yield i;
}
}
Test Runner
I've written this test for both these scenarios:
function runTest(testName, numIterations, funcToTest) {
console.log(`Running ${testName}...`);
let dummyCalculation;
const startTime = Date.now();
const initialMemory = process.memoryUsage();
const iterator = funcToTest(numIterations);
for (let val of iterator) {
dummyCalculation = numIterations - val;
}
const finalMemory = process.memoryUsage();
// note: formatNumbers can be found here: https://jsfiddle.net/onz1ozjq/
console.log(formatNumbers `Total time: ${Date.now() - startTime}ms`);
console.log(formatNumbers `Rss: ${finalMemory.rss - initialMemory.rss}`);
console.log(formatNumbers `Heap Total: ${finalMemory.heapTotal - initialMemory.heapTotal}`);
console.log(formatNumbers `Heap Used: ${finalMemory.heapUsed - initialMemory.heapUsed}`);
}
Running the Tests
Then when running these two like so:
const numIterations = 999999; // 999,999
console.log(formatNumbers `Running tests with ${numIterations} iterations...\n`);
runTest("Array test", numIterations, getNumberArray);
console.log("");
runTest("Generator test", numIterations, getNumberGenerator);
I get results similar to this:
Running tests with 999,999 iterations...
Running Array test...
Total time: 105ms
Rss: 31,645,696
Heap Total: 31,386,624
Heap Used: 27,774,632
Running Function generator test...
Total time: 160ms
Rss: 2,818,048
Heap Total: 0
Heap Used: 1,836,616
Note: I am running these tests on node v4.1.1 on Windows 8.1. I am not using a transpiler and I'm running it by doing node --harmony generator-test.js.
Question
The increased memory usage with an array is obviously expected... but why am I consistently getting faster results for an array? What's causing the slowdown here? Is doing a yield just an expensive operation? Or maybe there's something up with the method I'm doing to check this?
The terribly unsatisfying answer is probably this: Your ES5 function relies on features that (with the exceptions of let and const) have been in V8 since it was released in 2008 (and presumably for some time before, as I understand that what became V8 originated as part of Google's web crawler). Generators, on the other hand, have only been in V8 since 2013. So not only has the ES5 code had seven years to be optimized while the ES6 code has had only two, almost nobody (compared to the many millions of sites using code just like your ES5 code) is using generators in V8 yet, which means there has been very little opportunity to discover, or incentive to implement, optimizations for it.
If you really want a technical answer as to why generators are comparatively slow in Node.js, you'll probably have to dive into the V8 source yourself, or ask the people who wrote it.
In the OP's example, the generator will always be slower
While JS engine authors will continue working to improve performance, there are some underlying structural realities that virtually guarantee that, for the OP's test case, building the array will always be faster than using an iterator.
Consider that a generator function returns a generator object.
A generator object will, by definition, have a next() function, and calling a function in Javascript means adding an entry to your call stack. While this is fast, it's likely never going to be as fast as direct property access. (At least, not until the singularity.)
So if you are going to iterate over every single element in a collection, then a for loop over a simple array, which accesses elements via direct property access, is always going to be faster than a for loop over an iterator, which accesses each element via a call to the next() function.
As I'm writing this in January of 2022, running Chrome 97, the generator function is 60% slower than the array function using the OP's example.
Performance is use-case-dependent
It's not difficult to imagine scenarios where the generator would be faster. The major downside to the array function is that it must build the entire collection before the code can start iterating over the elements, whether or not you need all the elements.
Consider a basic search operation which will only access, on average, half the elements of the collection. In this scenario, the array function exposes its "Achilles' heel": it must build an array with all the results, even though half will never be seen. This is where a generator has the potential to far outstrip the array function.
To demonstrate this, I slightly modified the OP's use-case. I made the elements of the array slightly more expensive to calculate (with a little division and square root logic) and modified the loop to terminate at about the halfway mark (to mimic a basic search).
Setup
function getNumberArray(maxValue) {
const a = [];
for (let i = 0; i < maxValue; i++) {
const half = i / 2;
const double = half * 2;
const root = Math.sqrt(double);
const square = Math.round(root * root);
a.push(square);
}
return a;
}
function* getNumberGenerator(maxValue) {
for (let i = 0; i < maxValue; i++) {
const half = i / 2;
const double = half * 2;
const root = Math.sqrt(double);
const square = Math.round(root * root);
yield square;
}
}
let dummyCalculation;
const numIterations = 99999;
const searchNumber = numIterations / 2;
Generator
const iterator = getNumberGenerator(numIterations);
for (let val of iterator) {
dummyCalculation = numIterations - val;
if (val > searchNumber) break;
}
Array
const iterator = getNumberArray(numIterations);
for (let val of iterator) {
dummyCalculation = numIterations - val;
if (val > searchNumber) break;
}
With this code, the two approaches are neck-and-neck. After repeated test runs, the generator and array functions trade first and second place. It's not difficult to imagine that if the elements of the array were even more expensive to calculate (for example, cloning a complex object, making a REST callout, etc), then the generator would win easily.
Considerations beyond performance
While recognizing that the OP's question is specifically about performance, I think it's worth calling out that generator functions were not primarily developed as a faster alternative to looping over arrays.
Memory efficiency
The OP has already acknowledged this, but memory efficiency is one of the main benefits that generators provide over building arrays. Generators can build objects on the fly and then discard them when they are no longer needed. In its most ideal implementation, a generator need only hold one object in memory at a time, while an array must hold all of them simultaneously.
For a very memory-intensive collection, a generator would allow the system to build objects as they are needed and then reclaim that memory when the calling code moves on to the next element.
Representation of non-static collections
Generators don't have to resolve the entire collection, which free them up to represent collections that might not exist entirely in memory.
For example, a generator can represent collections where the logic to fetch the "next" item is time-consuming (such as paging over the results of a database query, where items are fetched in batches) or state-dependent (such as iterating over a collection where operations on the current item affect which item is "next") or even infinite series (such as a fractal function, random number generator or a generator returning all the digits of π). These are scenarios where building an array would be either impractical or impossible.
One could imagine a generator that returns procedurally generated level data for a game based on a seed number, or even to represent a theoretical AI's "stream of consciousness" (for example, playing a word association game). These are interesting scenarios that would not be possible to represent using a standard array or list, but where a loop structure might feel more natural in code.
FYI this question is ancient in internet terms and generators have caught up (at least when tested in Chrome) https://jsperf.com/generator-vs-loops1
Try replacing the 'let' in the generator function with a function scoped 'var'. It seems that the 'let' inside the loop incurs a lot of overhead. Only use let if you absolutely have to.
In fact, running this benchmark now, generators at ~2x faster.
I've modified the code slightly (moved let i) and here is the full gist: https://gist.github.com/antonkatz/6b0911c85ddadae39c434bf8ac32b340
On my machine, these are the results:
Running Array...
Total time: 4,022ms
Rss: 2,728,939,520
Heap Total: 2,726,199,296
Heap Used: 2,704,236,368
Running Generator...
Total time: 2,541ms
Rss: 851,968
Heap Total: 0
Heap Used: -5,073,968
I was very curious myself and could not find a proper answer. Thanks #David for providing the test code.

Javascript Memoization

Can someone please provide a simple function with memorization using only Javascript. I found a few articles online when googling, but I didn't see a lot on it. The best article that I found was this one:
http://alivedise.github.io/blog/2012/12/22/javascript-memorization/
I understand what caching is but, the example was way too complex for me. I was hoping that anyone here could please provide a simple function and call so that I can take that and begin to understand this more in depth.
Thanks
I think what you're looking for is memoization.
From Wikipedia:
memoization is an optimization technique used primarily to speed up computer programs by having function calls avoid repeating the calculation of results for previously processed inputs
There's a nice article here and another SO question here.
You'd normally use memoization to reduce the cost of repeatedly computing a result that will always be the same. Any performance improvement comes at the expense of allocating memory for the cached results.
A simple example in code:
var cachedResult;
function doHeavyCalculation()
{
if (typeof(cachedResult) !== 'undefined')
return cachedResult;
// no cached result available. calculate it, and store it.
cachedResult = /* do your computation */;
return cachedResult;
}
There are JavaScript frameworks that support memoizing any function, and they basically provide this boilerplate code for you in a reusable fashion by decorating a function.
I think you mean memoization, which basically means remembering what you have already calculated. The following is an algorithm for Fibonacci which uses memoization.
var cache = {1:1, 2:1};
function fib(n) {
if(!cache[n]) // Have we already calculated this value?
cache[n] = fib(n - 1) + fib(n - 2) // Calculate and store it
return cache[n]
}
I’m afraid all other answers use global variable, which is wrong. JavaScript offers better solution. Please notice the parentheses () after the function expression. It means that the function is fired immediately, and the result returned by the function (and assigned to the memo constant) Is another function, which make the computing itself, but which can use the cache as a variable from the context of the already fired function. The cache is accessible by the memo function only.
const memo = function () {
let cache = [];
return function (n) {
if (cache.includes(n)) { console.log("already in memory") }
else { console.log("first"); cache.push(n); }
}
}();
memo(7) //first
memo(7) //already in memory
memo(7) //already in memory
memo(1) //first
memo(1) //already in memory
keslert's Fibonacci example is a good one, here's one more for your understanding using edit distance as an example.
// Map<string, Map<string, number>>
const cache = new Map();
// a: string, b: string
function editDistance(a, b) {
if (a.length === 0) {
return b.length;
}
if (b.length === 0) {
return a.length;
}
let res = cache.getMap(a).get(b);
if (res !== undefined) {
return res;
}
res = Math.min(
editDistance(pop(a), pop(b)) + (last(a) === last(b) ? 1 : 0)
, editDistance(pop(a), b) + 1
, editDistance(a, pop(b)) + 1
);
cache.getMap(a).set(b, res);
return res;
}
It is worth to mention that for some cases, doing the computation directly is less costly than looking up the memory (cache). For example basic logical operation or a few step math.
To determine the exact case in detail, you need to know the mechanism used by the cache (the data structure, complexity of operation, even the medium of storage (i.e. are you using fast RAM or swapped to slow harddisk?)) which is implementation dependent on the browser / JavaScript engine.
source: https://github.com/beenotung/programming-class/blob/3f678ac48511d829149fb06c139e53cc2680ae82/edit-distance/edit-distance.ts
-- edit 2018 Mar 06, 13:56
In the example, the call to pop/1 function can also be cached as well.

sequential calls of methods asynchronously

I have a list of methods I am calling in a method, as follows:
this.doOneThing();
someOtherObject.doASecondThing();
this.doSomethingElse();
When this is synchronous, they are executed one after the other, which is required. But now I have someOtherObject.doASecondThing() as asynchronous, and I might turn doOneThing as async too. I could use a callback and call that.doSomethingElse from inside the callback:
var that = this;
this.doOneThing( function () {
someOtherObject.doASecondThing(function () {
that.doSomethingElse();
});
});
However, because the sequence is growing, it seems a little messy to have callbacks call each other, for some reason it makes the sequence not look as obvious as before, and the indentation might grow with the number of methods called in the sequence.
Is there a way to make this look better? I could use the observer pattern also but it doesn't make things very obvious either, in my opinion.
Thanks,
Continuations, and why they're causing callback spaghetti
Writing in callbacks forces you to write in sometime akin to "continuation-passing style" (CPS), an extremely powerful but difficult technique. It represents a total inversion of control, literally turning a computation "upside-down". CPS makes your code's structure explicitly reflect the control flow of your program (sometimes a good thing, sometimes a bad thing). In effect, you are explicitly writing down the stack out of anonymous functions.
As a prerequisite for understanding this answer, you may find this useful:
http://matt.might.net/articles/by-example-continuation-passing-style/
For example this is what you are doing:
function thrice(x, ret) {
ret(x*3)
}
function twice(y, ret) {
ret(y*2)
}
function plus(x,y, ret) {
ret(x+y)
}
function threeXPlusTwoY(x,y, ret) {
// STEP#1
thrice(x, // Take the result of thrice(x)...
function(r1) { // ...and call that r1.
// STEP#2
twice(y, // Take the result of twice(y)...
function(r2) { // ...and call that r2.
// STEP#3
plus(r1,r2, // Take r1+r2...
ret // ...then do what we were going to do.
)
}
)
}
)
}
threeXPlusTwoY(5,1, alert); //17
As you've complained about, this makes for fairly indented code, because closures are the natural way to capture this stack.
Monads to the rescue
One way to unindent CPS is to write "monadically" like in Haskell. How would we do that? One nice way of implementing monads in javascript is with the dot-chaining notation, similar to jQuery. (See http://importantshock.wordpress.com/2009/01/18/jquery-is-a-monad/ for an amusing diversion.) Or we can use reflection.
But first we need a way to "write down the plumbing", and THEN we can find a way abstract it away. Tragically it's sort of hard to write a generic monad syntax in javascript, so I will use lists to represent computations.
// switching this up a bit:
// it's now 3x+2x so we have a diamond-shaped dependency graph
// OUR NEW CODE
var _x = 0;
var steps = [
[0, function(ret){ret(5)},[]], //step0:
[1, thrice,[_x]], //step1: thrice(x)
[2, twice,[_x]], //step2: twice(x)
[3, plus,[1, 2]] //step3: steps[1]+steps[2] *
]
threeXPlusTwoX = generateComputation(steps);
//*this may be left ambiguous, but in this case we will choose steps1 then step2
// via the order in the array
That's kinda ugly. But we can make this UNINDENTED "code" work. We can worry about making it prettier later (in the last section). Here our purpose was to write down all the "necessary information". We'd like an easy way to write each "line", along with a context we can write them in.
Now we implement a generateComputation which generates some nested anonymous functions which would perform the above steps in-order if we executed it. This is what such an implementation would look like:
function generateComputation(steps) {
/*
* Convert {{steps}} object into a function(ret),
* which when called will perform the steps in order.
* This function will call ret(_) on the results of the last step.
*/
function computation(ret) {
var stepResults = [];
var nestedFunctions = steps.reduceRight(
function(laterFuture, step) {
var i = step[0]; // e.g. step #3
var stepFunction = step[1]; // e.g. func: plus
var stepArgs = step[2]; // e.g. args: 1,2
console.log(i, laterFuture);
return function(returned) {
if (i>0)
stepResults.push(returned);
var evalledStepArgs = stepArgs.map(function(s){return stepResults[s]});
console.log({i:i, returned:returned, stepResults:stepResults, evalledStepArgs:evalledStepArgs, stepFunction:stepFunction});
stepFunction.apply(this, evalledStepArgs.concat(laterFuture));
}
},
ret
);
nestedFunctions();
}
return computation;
}
Demonstration:
threeXPlusTwoX = generateComputation(steps)(alert); // alerts 25
sidenote: reduceRight semantics implies the steps on the right will be more deeply nested in functions (further in the future). FYI for those not familiar, [1,2,3].reduce(f(_,_), x) --> f(f(f(0,1), 2), 3), and reduceRight (due to poor design considerations) is actually equivalent to [1.2.3].reversed().reduce(...)
Above, generateComputation made a bunch of nested functions, wrapping them in one another in preparation, and when evaluated with ...(alert), unpeeled them one-by-one to feed into the computation.
sidenote: We have to use a hack because in the previous example, we used closures and variable names to implement CPS. Javascript does not allow sufficient reflection to do this without resorting to crafting a string and evaling it (ick), so we eschew functional style temporarily and opt for mutating an object that keeps track of all parameters. Thus the above more closely replicates the following:
var x = 5;
function _x(ret) {
ret(x);
}
function thrice(x, ret) {
ret(x*3)
}
function twice(y, ret) {
ret(y*2)
}
function plus(x,y, ret) {
ret(x+y)
}
function threeXPlusTwoY(x,y, ret) {
results = []
_x(
return function(x) {
results[0] = x;
thrice(x, // Take the result of thrice(x)...
function(r1) { // ...and call that r1.
results[1] = r1;
twice(y, // Take the result of twice(y)...
function(r2) { // ...and call that r2.
results[2] = r2;
plus(results[1],results[2], // Take r1+r2...
ret // ...then do what we were going to do.
)
}
)
}
)
}
)
}
Ideal syntax
But we still want to write functions in a sane way. How would we ideally like to write our code to take advantage of CPS, but while retaining our sanity? There are numerous takes in the literature (for example, Scala's shift and reset operators are just one of the many ways to do so), but for sanity's sake, let's just find a way to make syntactic sugar for regular CPS. There are some possible ways to do it:
// "bad"
var _x = 0;
var steps = [
[0, function(ret){ret(5)},[]], //step0:
[1, thrice,[_x]], //step1: thrice(x)
[2, twice,[_x]], //step2: twice(x)
[3, plus,[1, 2]] //step3: steps[1]+steps[2] *
]
threeXPlusTwoX = generateComputation(steps);
...becomes...
If the callbacks are in a chain, we can easily feed one into the next without worrying about naming. These functions only have one argument: the callback argument. (If they didn't, you could curry the function as follows on the last line.) Here we can use jQuery-style dot-chains.
// SYNTAX WITH A SIMPLE CHAIN
// ((2*X) + 2)
twiceXPlusTwo = callbackChain()
.then(prompt)
.then(twice)
.then(function(returned){return plus(returned,2)}); //curried
twiceXPlusTwo(alert);
If the callbacks form a dependency tree, we can also get away with jQuery-style dot-chains, but this would defeat the purpose of creating a monadic syntax for CPS, which is to flatten the nested functions. Thus we won't go into detail here.
If the callbacks form a dependency acyclic graph (for example, 2*x+3*x, where x is used twice) we would need a way to name the intermediate results of some callbacks. This is where it gets interesting. Our goal is to try to mimic the syntax at http://en.wikibooks.org/wiki/Haskell/Continuation_passing_style with its do-notation which "unwraps" and "rewraps" functions into and out of CPS. Unfortunately the [1, thrice,[_x]] syntax was the closest we could easily get to that (and not even close). You could code in another language and compile to javascript, or using eval (queue the ominous music). A bit overkill. Alternatives would have to use strings, such as:
// SUPER-NICE SYNTAX
// (3X + 2X)
thriceXPlusTwiceX = CPS({
leftPart: thrice('x'),
rightPart: twice('x'),
result: plus('leftPart', 'rightPart')
})
You can do this with only a few tweaks to the generateComputation I described. First you adapt it to use logical names ('leftPart', etc.) rather than numbers. Then you make your functions actually lazy objects which behave like:
thrice(x).toListForm() == [<real thrice function>, ['x']]
or
thrice(x).toCPS()(5, alert) // alerts 15
or
thrice.toNonCPS()(5) == 15
(You would do this in an automated way with some kind of decorator, not manually.)
sidenote: All your callback functions should follow the same protocol about where the callback parameter is. For example if your functions begin with myFunction(callback, arg0, arg1, ...) or myFunction(arg0, arg1, ..., callback) they might not be trivially compatible, though if they aren't presumably you could do a javascript reflection hack to look at the source code of the function and regex it out, and thus not have to worry about it.
Why go through all that trouble? This allows you to mix in setTimeouts and prompts and ajax requests without suffering from "indent hell". You also get a whole bunch of other benefits (like being able to write a 10-line nondeterministic-search sudoku solver, and implementing arbitrary control-flow operators) which I will not go into here.

Categories

Resources