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.
Related
Consider the following code snippet:
function getMaxAge(arr, id, max) {
let found = false
for (let i = 0; i < arr.length; i++) {
const val = arr[i];
if (val.id === id) {
found = true
if (max > val.age) val.age = max
}
return val
})
if (found === false) {
arr.push({
id: id,
age: max
})
}
return arr
}
Now watch the same logic using map:
function getMaxAge(arr, id, max) {
let found = false
arr = arr.map((val, index) => {
if (val.id === id) {
found = true
if (max > val.age) val.age = max
}
return val
})
if (found === false) {
arr.push({
id: id,
age: max
})
}
return arr
}
What I am interested in is not the logic of the function, but whether the use of Array.map is justified for this scenario?
I personally avoid manual iteration whenever possible. Iterator methods are a concept build upon manual iteration and let objects themself decide how iteration is done. So if you have the option, I'd say avoid manual iteration. There are a few things to keep in mind.
Generally speaking I would avoid iteration methods when altering the collection while iterating. Because you don't know the implementation you don't fully know what's happening when adding/removing elements to the collection during iteration.
When you explicitly need to work with the index (and not the element) it might be worth the trouble to manually do iteration.
With the above being said, it's good to know what iteration methods are available to you, since different methods fulfil different tasks. Knowing which method to use in which scenario will result in better performance and cleaner code.
Taking your example I'd change it to the following:
function getMaxAge(arr, id, max) {
arr = arr.slice();
const matchesId = arr.filter(val => val.id == id),
found = matchId.length;
matchesId.filter(val => val.age < max)
.forEach(val => (val.age = max));
if (!found) arr.push({id: id, age: max});
return arr;
}
if we add your question in comment
"what I wanted to ask is whether the cost of calling .map (which creates another copy) is worth the effort of typing arr[i]?" to the original question and assume you're in dilemma between iterating functions vs. manual iteration in a for loop with respect to performance, then i guess we could answer roughly like this:
for small arrays - it doesn't matter.
for big arrays - for loop is definitely better
for big arrays and critical performance (where you want to keep your code below some fps threshold time), you'll want to pay attention to many different things: caching variable values (arr.length for example), do object pooling whenever possible, offload work to background workers, etc...
but have on mind saying that premature optimization is the root of all evil. in other words, start with most readable functions (using them properly, of course :P ) and optimize as needed for particular cases you need.
I'm creating a few specific functions for a compiler I'm working on, But certain restrictions within the compiler's nature will prevent me from using native JavaScript methods like Array.prototype.pop() to perform array pops...
So I decided to try and write some rudimentary pseudo-code to try and mimic the process, and then base my final function off the pseudo-code... But my tests seem to fail... based on the compiler's current behavior, it will only allow me to use array.length, array element assignments and that's about it... My code is below...
pop2 = function(arr) {
if(arr.length>0){
for(var w=undefined,x=[],y=0,z=arr.length;y<=z;y++){
y+1<z?(x[y]=arr[y]):(w=arr[y],arr=x);
}
}
return w;
}
Arr = [-1,0,1,2];
// Testing...
console.log(pop2(Arr)); // undefined... should be 2
console.log(Arr); // [-1,0,1,2]... should be [-1,0,1]
I'm trying to mimic the nature of the pop function but can't seem to put my finger on what's causing the function to still provide undefined and the original array... undefined should only return if an initial empty array is sent, just like you would expect with a [].pop() call...
Anyone have any clues as to how I can tailor this code to mimic the pop correctly?
And while I have heard that arr.splice(array.length-1,1)[0]; may work... the compiler is currently not capable of determining splice or similar methods... Is it possible to do it using a variation of my code?
Thanks in advance...
You're really over-thinking [].pop(). As defined in the specs, the process for [].pop() is:
Get the length of the array
If the length is 0
return undefined
If length is more than 0
Get the item at length - 1
Reduce array.length by 1
Return item.
(... plus a few things that the JavaScript engine needs to do behind the scenes like call ToObject on the array or ensure the length is an unsigned 32-bit integer.)
This can be done with a function as simple as the one below, there's not even a need for a loop.
function pop(array) {
var length = array.length,
item;
if (length > 0) {
item = array[length - 1];
array.length -= 1;
}
return item;
}
Edit
I'm assuming that the issue with the compiler is that Array.prototype.pop isn't understood at all. Re-reading your post, it looks like arrays have a pop method, but the compiler can't work out whether the variable is an array or not. In that case, an even simpler version of this function would be this:
function pop(array) {
return Array.prototype.pop.call(array);
}
Try that first as it'll be slightly faster and more robust, if it works. It's also the pattern for any other array method that you may need to use.
With this modification, it works:
http://jsfiddle.net/vxxfxvpL/1/
pop2 = function(arr) {
if(arr.length>0){
for(var w=undefined,x=[],y=0,z=arr.length;y<=z;y++){
if(y+1<z) {
(x[y]=arr[y]);
} else {
(w=arr[y],arr=x);
break;
}
}
}
return w;
}
Arr = [-1,0,1,2];
// Testing...
console.log(pop2(Arr)); // 2
The problem now is to remove the last element. You should construct the original array again without last element. You will have problems with this because you can't modify the original array. That's why this tasks are maded with prototype (Array.prototype.pop2 maybe can help you)
In Javascript what is the best way to handle scenarios when you have a set of arrays to perform tasks on sets of data and sometimes you do not want to include all of the arrays but instead a combination.
My arrays are labeled in this small snippet L,C,H,V,B,A,S and to put things into perspective the code is around 2500 lines like this. (I have removed code notes from this post)
if(C[0].length>0){
L=L[1].concat(+(MIN.apply(this,L[0])).toFixed(7));
C=C[1].concat(C[0][0]);
H=H[1].concat(+(MAX.apply(this,H[0])).toFixed(7));
V=V[1].concat((V[0].reduce(function(a,b){return a+b}))/(V[0].length));
B=B[1].concat((MAX.apply(this,B[0])-MIN.apply(this,B[0]))/2);
A=A[1].concat((MAX.apply(this,A[0])-MIN.apply(this,A[0]))/2);
D=D[1].concat((D[0].reduce(function(a,b){return a+b}))/(D[0].length));
S=S[1].concat((S[0].reduce(function(a,b){return a+b}))/(S[0].length));
}
It would seem counter-productive in this case to litter the code with tones of bool conditions asking on each loop or code section if an array was included in the task and even more silly to ask inside each loop iteration with say an inline condition as these would also slow down the processing and also make the code look like a maze or rabbit hole.
Is there a logical method / library to ignore instruction or skip if an option was set to false
All I have come up with so far is kind of pointless inline thing
var op=[0,1,1,0,0,0,0,0]; //options
var L=[],C=[],H=[],V=[],B=[],A=[],D=[],S=[];
op[0]&&[L[0]=1];
op[1]&&[C[0]=1,console.log('test, do more than one thing')];
op[2]&&[H[0]=1];
op[3]&&[V[0]=1];
op[4]&&[B[0]=1];
op[5]&&[A[0]=1];
op[6]&&[A[0]=1];
It works in that it sets only C[0] and H[0] to 1 as the options require, but it fails as it needs to ask seven questions per iteration of a loop as it may be done inside a loop. Rather than make seven versions of the the loop or code section, and rather than asking questions inside each loop is there another style / method?
I have also noticed that if I create an array then at some point make it equal to NaN rather than undefined or null the console does not complain
var L=[],C=[],H=[],V=[],B=[],A=[],D=[],S=[];
L=NaN;
L[0]=1;
//1
console.log(L); //NaN
L=undefined;
L[0]=1
//TypeError: Cannot set property '0' of undefined
L=null
L[0]=1
//TypeError: Cannot set property '0' of null
Am I getting warmer? I would assume that if I performed some math on L[0] when isNaN(L)===true that the math is being done but not stored so the line isn't being ignored really..
If I understand what you want I would do something like this.
var op = [...],
opchoice = {
//these can return nothing, no operation, or a new value.
'true': function(val){ /*operation do if true*/ },
'false': function(val){ /*operation do if false*/ },
//add more operations here.
//keys must be strings, or transformed into strings with operation method.
operation: function(val){
//make the boolean a string key.
return this[''+(val == 'something')](val);
}
};
var endop = [];//need this to prevent infinite recursion(loop).
var val;
while(val = op.shift()){
//a queue operation.
endop.push(opchoice.operation(val));
}
I'm sure this is not exactly what you want, but it's close to fulfilling the want of not having a ton of conditions every where.
Your other option is on every line do this.
A = isNaN(A) ? A.concat(...) : A;
Personally I prefer the other method.
It looks like you repeat many of the operations. These operations should be functions so at least you do not redefine the same function over and over again (it is also an optimization to do so).
function get_min(x)
{
return +(MIN.apply(this, a[0])).toFixed(7);
}
function get_max(x)
{
return +(MAX.apply(this, a[0])).toFixed(7);
}
function get_average(x)
{
return (x[0].reduce(function(a, b) {return a + b})) / (x[0].length);
}
function get_mean(x)
{
return (MAX.apply(this, x[0]) - MIN.apply(this, x[0])) / 2;
}
if(C[0].length > 0)
{
L = L[1].concat(get_min(L));
C = C[1].concat(C[0][0]);
H = H[1].concat(get_max(H));
V = V[1].concat(get_average(V));
B = B[1].concat(get_mean(B));
A = A[1].concat(get_mean(A);
D = D[1].concat(get_average(D));
S = S[1].concat(get_average(S));
}
You could also define an object with prototype functions, but it is not clear whether it would be useful (outside of putting those functions in a namespace).
In regard to the idea/concept of having a test, what you've found is probably the best way in JavaScript.
op[0] && S = S[1].concat(get_average(S));
And if you want to apply multiple operators when op[0] is true, use parenthesis and commas:
op[3] && (V = V[1].concat(get_average(V)),
B = B[1].concat(get_mean(B)),
A = A[1].concat(get_mean(A));
op[0] && (D = D[1].concat(get_average(D)),
S = S[1].concat(get_average(S)));
However, this is not any clearer, to a programmer, than an if() block as shown in your question. (Actually, many programmers may have to read it 2 or 3 times before getting it.)
Yet, there is another solution which is to use another function layer. In that last example, you would do something like this:
function VBA()
{
V = V[1].concat(get_average(V));
B = B[1].concat(get_mean(B));
A = A[1].concat(get_mean(A));
}
function DS()
{
D = D[1].concat(get_average(D));
S = S[1].concat(get_average(S));
}
op = [DS,null,null,VBA,null,null,...];
for(key in op)
{
// optional: if(op[key].hasOwnProperty(key)) ... -- verify that we defined that key
if(op[key])
{
op[key](); // call function
}
}
So in other words you have an array of functions and can use a for() loop to go through the various items and if defined, call the function.
All of that will very much depend on the number of combinations you have. You mentioned 2,500 lines of code, but the number of permutations may be such that writing it one way or the other will possibly not reduce the total number of lines, but it will make it easier to maintain because many lines are moved to much smaller code snippet making the overall program easier to understand.
P.S. To make it easier to read and debug later, I strongly suggest you put more spaces everywhere, as shown above. If you want to save space, use a compressor (minimizer), Google or Yahoo! both have one that do a really good job. No need to write your code pre-compressed.
I have a requirement that the user can provide arbitrary statements which can be stored in a function and called later to get a return value. A simple example of this is that userInput might be
var x = 10;
x;
I would store this via
var callback = function() {
return eval(userInput);
}
and then running callback() returns 10 as expected.
However, I also need to support the case with an explicit return statement, ie userInput might be
var x = 10;
return x;
In this case the eval method above will fail with SyntaxError: return not in function. Instead I could store callback as
var callback = new Function(userInput);
My issue is that I would like to combine these two approaches according the rule 'get explicit return value otherwise get the result of the last executed statement'. In particular this can't be done with analysis of the code at callback creation time as the user could potentially do something odd like
if(envVar < 10)
return a;
b * 0.5;
which combines both.
Any thoughts on how to structure the creation of the callback function to accommodate these possible inputs? Unfortunately it is not possible to enforce one style or the other on the user.
UPDATE to answer some of the comments below.
One solution suggested is to search for a return token and choose between new Function and eval. This doesn't work for my last example, see http://jsfiddle.net/ZGb6z/2/ - out4 should be "no" but ends up being undefined as the last executed statement is not returned.
Another suggestion is to modify the user input to add an explicit return on the last line if one is not found. Unfortunately it's not possible to know which statement will be executed last. Consider
var x = 10;
switch(x) {
case 10:
100;
break;
default:
200;
break;
}
When called it should return 100, but it would take some serious analysis to determine where to put returns for arbitrary code.
Just use a try catch, manipulating the input will be very painful for you, and try catch can't really make your code any more obtuse at this point.
var failback = function () {
try {
return eval(userInput);
} catch (e) {
return Function(userInput);
}
};
What I would really suggest is investing in a parser, kind of like how Angular does it. That kind of thing would prevent your users from doing whatever the hell they want, introducing attack vectors, yadda, yadda, yadda.
Either manage your expectations or manage your user's expectations. eval and new Function() are not suitable for your requirements if you require mixed usage of explicit and non-explicit return statements in the same user-input. You will continue to find issues following either of these routes.
Simply searching for the word return is not sufficient either... var returning = true; or var a = 'return'; or /* return true */ true; will all throw false positives.
Managing your expectations: To do such a thing you require a form of lexer and parser, at which point you can do away with eval entirely and execute your own safe functions based on the parsed input. This is the best approach when execution of user input has to occur anyway as you can ensure that nothing gets executed you do not wish to permit. If you want to cover these sort of edge cases and permit strange user input then you must be prepared to increase the size and development time of your application. I have built a few applications executing user generated code and have always come to the conclusion this is the correct route to go down.
Managing your user's expectations: Provide a guide, tell them not to mix explicit returns with non-explicit returns, these are strange coding practices anyway. Better yet explicitly tell them to include or omit the return statement. There is no shame in asking your users to follow them, especially if it allows you to improve their experience elsewhere.
There I was thinking I'd only see problems like this at the code golf stack exchange :)
My solution is here: http://jsfiddle.net/hKq87/1
It essentially replaces the 'return' statement with an exception that has a special string prefixed to it. If we see that string, we know we are actually returning a value, and return it rather than re-raising the exception.
The reason I chose to throw an exception rather than replace the return statement with a function call was because it is hard to know where the JS code evaluated for the return really ends. It could be split across multiple lines, contain several special characters and may not even have the optional semi-colon at the end. So I concatenate a string to whatever the value being returned is and throw it, as the throw keyword doesn't require it's argument to be wrapped in parentheses.
In addition, throwing exceptions provides me a convenient way to immediately terminate execution of the code block, without halting other JS execution.
Here is the callback method:
var callback = function(userInput) {
var returned = undefined;
userInput = userInput.replace(/(^|[\(\\)[\]{};,\s])return(\s*[^\s;])?/gi, function(m, b, e){
return b + " throw '__RETURNED_VALUE'" +
(e !== undefined ? "+" + e : "");
});
try {
returned = eval(userInput);
} catch (e) {
if (e.indexOf("__RETURNED_VALUE") == 0) {
returned = e.substring("__RETURNED_VALUE".length) || undefined;
}
else {
throw e;
}
}
return returned;
}
The regex above accounts for variables that may end with the string "return", that we would not want to replace as it is not a return statement. It also allows for return statements within braces, without trailing semi-colons or at the very beginning/end.
One issue with the current method is that you can not use the (rare) comma operator in a return statement, or expect numerical values to be returned correctly. The last test case in the jsfiddle demonstrates this. An example from here:
//original
return 5 * 2 + 3, 22;
//modified
throw '__RETURNED_VALUE='+ 5 * 2 + 3, 22;
//apply * operator
throw '__RETURNED_VALUE='+ 10 + 3, 22;
//apply + operators
throw '__RETURNED_VALUE=103', 22;
//apply , operator
throw 22;
This problem can be avoided by completely eliminating the prefix '__RETURNED_VALUE=' and just replacing 'return' with 'throw'. However, this means that the code provided must run without throwing exceptions, which I thought to be a harder constraint than just crafting return statements to be simple (avoiding comma operators, non-parenthesized arithmetic, etc.). In addition, if a user ever creates a return statement that we can't handle with the current code, we conveniently throw an exception for it so it easily comes to our attention.
jsFiddle Demo
Lets assume your user can be a little smarter than the average bear. We are going to ask them to specifically provide an initial value and then a callback with an expression for that value.
The main benefit of doing this is that you avoid eval, and actually have a nice implementation that is re-usable as opposed to being subject to later refactoring.
This way also provides a separation of where the input comes from and where the examination comes from. Although the provided example only shows integer input, really it could be another call with absolutely no knowledge of the value aside that it needs to conform to the callback logic.
function expression(x,callback){
return callback(x);
}
out1.innerHTML = expression(8,function(x){
return x;
});
out2.innerHTML = expression(10,function(x){
return x;
});
out3.innerHTML = expression(10,function(x){
if(x === 10) return "yes"; "no";
});
out4.innerHTML = expression(8,function(x){
return x === 10 ? "yes" : "no";
});
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.