Single frame continuations proposal

Kris Zyp kris at sitepen.com
Mon Apr 5 11:36:34 PDT 2010


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
 


On 4/5/2010 10:26 AM, David Herman wrote:
> [BTW, your quoted text got garbled.]
>
>> In order to utilize leverage continuations with a function that
>> execute multiple we would need to eliminate single-shot
>> restriction. You could then create some library function that
>> passed the continuation to the setInterval function to do
>> something like: var toggle = true; intervalling->(INTERVAL);
>> elt.style.background =...
>
> Your answers keep leaving out the definition of the function that
> you're calling via `->', which is supposed to be the part that
> creates the requisite object with `continueWith' etc. Examples
> don't do much good if they skip the part they were supposed to
> illustrate!
>
>> But in this case, using the yielding call would be confusing and
>> provide very little benefit. This is definitely not the type of
>> scenario that it is designed for, normal anon functions work
>> great here. This is geared for when a callback is used to execute
>> the remainder of a sequence after the completion of an
>> asynchronous that we see so often in JavaScript.
>
> I know. I wanted an example that had *both* asynchronous *and*
> synchronous statements, to understand the control flow of your
> proposal better, but I also was trying to keep it short. Maybe
> you'd prefer this example, which more clearly separates the parts
> that would be expressed with `->':
>
> function setup() { getThreeThings(URL1, URL2, URL3); alert("done
> setup!"); }
>
> function getThreeThings(url1, url2, url3) { getAndFrob(url1);
> getAndMunge(url2); getAndGrok(url3); }
>
> function getAndFrob(url) { var xhr = new SimpleXHR(url);
> xhr.get(function(data) { window.setTimeout(function() {
> frob(data); }, TIMEOUT); }); }
>
> function getAndMunge(url) { var xhr = new SimpleXHR(url);
> xhr.get(function(data) { window.setTimeout(function() {
> munge(data); }, TIMEOUT); }); }
>
> function getAndGrok(url) { var xhr = new SimpleXHR(url);
> xhr.get(function(data) { window.setTimeout(function() {
> grok(data); }, TIMEOUT); }); }
>
> Would you mind writing out how to express this program with your
> proposal? I'm just trying to understand. If you can help me with
> some examples I think I maybe able to help clarify your ideas.

OK, here is some code:

    function setup() {
        getThreeThings->(URL1, URL2, URL3);
        alert("done setup!");
    }
 
    function getThreeThings(url1, url2, url3) {
        getAndFrob->(url1);
        getAndMunge->(url2);
        getAndGrok->(url3);
    }
 
    function getAndFrob(url) {
        var xhr = new SimpleXHR(url);
        var data = xhr.get->();
        sleep->(TIMEOUT);
        return frob(data);
    }
 

Translated:

function setup(){
  var $result = getThreeThings(URL1, URL2, URL3);
  return $handleResult($result);
  function $handleResult($result){
    if($result && typeof $result.continueWith === "function"){
      var $waiting = true;
      return $result.continueWith(function($resumeNextFrame){
         if($waiting){
           $waiting = false;
            return $handleResult($resumeNextFrame());
          }
          throw new Error("The stack can not be resumed again");
      });
    }
    else {
      // execute the continuation of the function
      alert("done setup!");
    }
  }
}

function getThreeThings(url1, url2, url3) {
  var $codePointer = 0;
  var $result = getAndFrob(url1);
  return $handleResult($result);
  function $handleResult($result){
    if($result && typeof $result.continueWith === "function"){
      var $waiting = true;
      return $result.continueWith(function($resumeNextFrame){
         if($waiting){
           $waiting = false;
            return $handleResult($resumeNextFrame());
          }
          throw new Error("The stack can not be resumed again");
      });
    }
    else {
      // execute the continuation of the function
      if($codePointer == 0){
        $codePointer++;
        return $handleResult(getAndMunge(url2));
      }
      if($codePointer == 1){
        return $handleResult(getAndGrok(url3));
      }

    }
  }
}

function getAndFrob(url) {
  var $codePointer = 0;
  var xhr = new SimpleXHR(url);
  var data = xhr.get();
  return $handleResult(data);
  function $handleResult($result){
    if($result && typeof $result.continueWith === "function"){
      var $waiting = true;
      return $result.continueWith(function($resumeNextFrame){
         if($waiting){
           $waiting = false;
            return $handleResult($resumeNextFrame());
          }
          throw new Error("The stack can not be resumed again");
      });
    }
    else {
      // execute the continuation of the function
      if($codePointer == 0){
        $codePointer++;
        return $handleResult(sleep(INTERVAL));
      }
      if($codePointer == 1){
        return frob(data);
      }

    }
  }
}


getAndFrob, getAndMange, and getAndGrok are all basically the same.

// very simple continuation implementation
function Continuation(){
  var resolvedValue;
  var bottomFrame = function(){
    return resolvedValue;
  };
  var continuation = {
    continueWith: function(next){
      var nextFrame = bottomFrame;
      bottomFrame = function(){
        return next(nextFrame);
      }
      return continuation;
    },
    resume: function(value){
      resolvedValue = value;
      bottomFrame();
    }
  };
  return continuation;
}
// sleep function that uses a continuation
function sleep(ms){
  var continuation = new Continuation();
  setTimeout(function(){
    continuation.resume();
  }, ms);
  return continuation;
}
function SimpleXHR(url){
  var xhr = new XMLHttpRequest();
  return {
    get: function(){
      var continuation = new Continuation();
      xhr.open("GET", url);
      xhr.onload = continuation.resume;
      xhr.send();
      return continuation;
    }
  };
}





Also, if you want to see what this would look like with the "yield"
keyword based alternative:


    var startCoroutine = simpleContinuationHandler;
    function setup() {
        yield getThreeThings(URL1, URL2, URL3);
        alert("done setup!");
    }
 
    function getThreeThings(url1, url2, url3) {
        yield getAndFrob(url1);
        yield getAndMunge(url2);
        yield getAndGrok(url3);
    }
    /* Note these could be done in parralel with:
    function getThreeThings(url1, url2, url3) {
        var frobbed = getAndFrob(url1);
        var munged = getAndMunge(url2);
        var grokked = getAndGrok(url3);
        yield frobbed, yield munged, yield grokked;
    }
    */
 
    function getAndFrob(url) {
        var xhr = new SimpleXHR(url);
        var data = yield xhr.get();
        yield sleep(TIMEOUT);
        return frob(data);
    }
... 

Translated:

function setup(){
  var $pointer = 0; // used to keep track of where we are
  // create the controller
  var $controller = {
    suspended: true,
    resume: function(getValue){
      var nextValue;
      if(getValue){
        nextValue = getValue();
      }
      if($pointer == 0){
        // start of function
        $pointer++;
        // execute until the yield operator and return the value
passed to yield
        return getThreeThings(URL1, URL2, URL3);
      }
      if($pointer == 1){
        // continuation from the yield
        $pointer++;
        // continue execution until the return operator
        $controller.suspended = false; // no more yield, function is done
        alert("done setup!");
        return;
      }
      if($pointer > 1){
        throw new Error("Can not resume a function that has returned");
      }
    }
  };
  // call the startCoroutine function
  return startCoroutine($controller);
}

function getThreeThings(url1, url2, url3) {
  var $pointer = 0; // used to keep track of where we are
  // create the controller
  var $controller = {
    suspended: true,
    resume: function(getValue){
      var nextValue;
      if(getValue){
        nextValue = getValue();
      }
      if($pointer == 0){
        // start of function
        $pointer++;
        // execute until the yield operator and return the value
passed to yield
        return getAndFrob(url1);
      }
      if($pointer == 1){
        // continuation from the yield
        $pointer++;
        return getAndMunge(url2);
      }
      if($pointer == 2){
        // continuation from the yield
        $pointer++;
        // continue execution until the return operator
        $controller.suspended = false; // no more yield, function is done
        return getAndGrok(url3);
      }

      if($pointer > 2){
        throw new Error("Can not resume a function that has returned");
      }
    }
  };
  // call the startCoroutine function
  return startCoroutine($controller);
}


function getAndFrob(url) {
  var $pointer = 0; // used to keep track of where we are
  // create the controller
  var $controller = {
    suspended: true,
    resume: function(getValue){
      var nextValue;
      if(getValue){
        nextValue = getValue();
      }
      if($pointer == 0){
        // start of function
        $pointer++;
        // execute until the yield operator and return the value
passed to yield
        var xhr = new SimpleXHR(url);
        return xhr.get();
      }
      if($pointer == 1){
        // continuation from the yield
        $pointer++;
        var data = nextValue;
        return sleep(TIMEOUT);
      }
      if($pointer == 2){
        // continuation from the yield
        $pointer++;
        // continue execution until the return operator
        $controller.suspended = false; // no more yield, function is done
        return frob(data);
      }

      if($pointer > 2){
        throw new Error("Can not resume a function that has returned");
      }
    }
  };
  // call the startCoroutine function
  return startCoroutine($controller);
}



// Implementation of the continueWith proposal using the
yield/startCoroutine proposal

function simpleContinuationHandler(controller){
  function handleResult(value){
      if(!controller.suspended){
         return value;
      }
      if(value && typeof value.continueWith === "function"){
        var waiting = true;
        return value.continueWith(function(resume){
           if(!waiting){
              throw new Error("can't resume stack");
           }
           waiting = false;
           return handleResult(resume());
        });
      }
      else{
        return handleResult(controller.resume(function(){
          return value;
        }));
      }
 
  }
  return handleResult(controller.resume());

}

And the library functions defined above (Continuation, sleep, and
SimpleXHR) would work with this startCoroutine implementation and
translation.

>
>> Do you prefer basing single-frame continuations on new non-latin
>> character syntax instead of using the "yield" keyword (hadn't
>> realized it was already reserved in ES5 strict-mode when I did
>> the first proposal)?
>
> I don't follow you. Non-latin?
>
> Dave
>
>

I just meant "yield" vs "->()".

- -- 
Kris Zyp
SitePen
(503) 806-1841
http://sitepen.com
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (MingW32)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
 
iEYEARECAAYFAku6LbEACgkQ9VpNnHc4zAzEBgCeM+TI39RYjLduBx4RTDaVcfMx
UfMAn1SDT3GWdKtxl43xBQkX46nq1neC
=Qwpo
-----END PGP SIGNATURE-----



More information about the es-discuss mailing list