Search code examples
javascriptcontinuations

Emulate/use Continuations in JavaScript?


I have a function that computes product of numbers in an array. The function should work like this

function prod (array){
//compute and return product
}

var arr = [1,2,3,0,4,5,0,6,7,8,0,9];

the function call:
prod(arr); //should return 6
prod(arr); //should return 20
prod(arr); //should return 336 (6*7*8)
prod(arr); //should return 9
prod(arr); //should return 0
prod(arr); //should return 0
prod(arr); //should return 0

In scheme, this is done with continuations, by storing previous state of the function (state of the function is captured just before its exit point) see this

So, in short, I want the javascript function return different values at different times with same parameter passed everytime.

JavaScript is a well designed language, so I hope there must be something which can emulate this. If there happens to be nothing in JS to do it, I do not mind to conclude with failure and move on. So, feel free to say its impossible.

Thanks.


Solution

  • JavaScript is not capable of supporting continuations: it lacks tail-calls.

    Generally I would write this to use a "queue" of sorts, although CPS is also do-able (just have a finite stack :-) Note that other state can also be captured in the closure, making it an "explicit continuation" of sorts ... in a very gross sense.

    Example using a closure and a queue:

    function prodFactory (array){
       // dupe array first if needed, is mutated below.
       // function parameters are always locally scoped.
       array.unshift(undefined)  // so array.shift can be at start
       // also, perhaps more closured state
       var otherState
       // just return the real function, yippee!
       return function prod () {
          array.shift()
          // do stuff ... e.g. loop array.shift() and multiply
          // set otherState ... eat an apple or a cookie
          return stuff
       }
    }
    
    var prod = prodFactory([1,2,3,0,4,5,0,6,7,8,0,9])
    
            // array at "do stuff", at least until "do stuff" does more stuff
    prod()  // [1,2,3,0,4,5,0,6,7,8,0,9]
    prod()  // [2,3,0,4,5,0,6,7,8,0,9]
    prod()  // [3,0,4,5,0,6,7,8,0,9]
    

    Happy coding.


    "Finished implementation". Although this particular problem can avoid array mutation and just use an index: the same concepts apply. (Well, slightly different. With just an index the closed over variable would be altered, whereas with this approach an object is mutated.)

    function prodFactory (array) {
       array = array.slice(0)
       return function prod () {
          var p = 1
          for (var n = array.shift(); n; n = array.shift()) {
            p *= n
          }
          return p
       }
    }
    
    var prod = prodFactory([1,2,3,0,4,5,0,6,7,8,0,9])
    
    prod()  // 6
    prod()  // 20
    prod()  // 336