Subject: Re: Harmony - proxies | asynchronous

Brendan Eich brendan at mozilla.com
Tue Sep 6 11:46:58 PDT 2011


On Sep 6, 2011, at 10:10 AM, Dean Landolt wrote:

> On Tue, Sep 6, 2011 at 12:37 PM, John J Barton <johnjbarton at johnjbarton.com> wrote:
> 
>  I was more thinking along the lines of better support for async programming that does not attempt to look sync (I have no idea what that means).
> 
> 
> I'm also curious what "better support for async programming" looks like -- that always seem to boil down to a `wait` construct -- which it's already been established is not enabled by harmony generators.

Hi Dean, I hope you don't mind if I quibble here: generators do enable (as in make possible, but not fully implement) async or deferred functions. You need a scheduler and an event loop concurrency model in addition to generators for the full monte, but generators are co-expressive with the control-effect part of async or deferred functions.

Here is Dave Herman's desugaring write-up:

======================================================================

Library for creating intrinsic deferred objects
-----------------------------------------------

function IntrinsicDeferred(generator) {
   this.state = "newborn";
   this.generator = generator;
   this.callbacks = [];
   this.errbacks = [];
   this.completion = null;
   this.continue(void 0, true);
}

IntrinsicDeferred.prototype = {
   continue: function(value, normal) {
       if (this.state === "running" || this.state === "finished")
           throw new Error("illegal state");
       this.state = "running";
       let received;
       try {
           received = normal ? this.generator.send(value)
                             : this.generator.throw(value);
       } catch (e) {
           if (isStopIteration(e))
               this.callback(e.value);
           else
               this.errback(e);
           return;
       }
       let { awaited, callback, errback } = received;
       awaited.then(callback, errback);
       return;
   },
   then: function(cb, eb) {
       if (this.state === "finished") {
           if (this.completion.type === "return" && cb)
               cb(this.completion.value);
           if (this.completion.type === "error" && eb)
               eb(this.completion.value);
           return;
       }
       if (cb)
           this.callbacks.push(cb);
       if (eb)
           this.errbacks.push(eb);
   },
   createCallback: function(used) {
       let D = this;
       return function(value) {
           if (used.value)
               throw new Error("cannot reuse continuation");
           used.value = true;
           D.continue(value, true);
       };
   },
   createErrback: function(used) {
       let D = this;
       return function(value) {
           if (used.value)
               throw new Error("cannot reuse continuation");
           used.value = true;
           D.continue(value, false);
       };
   },
   await: function(awaited) {
       this.state = "suspended";
       let used = { value: false };
       return {
           awaited: awaited,
           callback: this.createCallback(used),
           errback: this.createErrback(used)
       };
   },
   cancel: function(value) {
       if (this.state === "running" || this.state === "finished")
           throw new Error("illegal state");
       this.state = "running";
       try {
           this.generator.close();
       } finally {
           this.errback(value);
       }
   },
   callback: function(value) {
       this.state = "finished";
       this.completion = { type: "return", value: value };
       let a = this.callbacks, n = a.length;
       for (let i = 0; i < n; i++) {
           try {
               let cb = a[i];
               cb(value);
           } catch (ignored) { }
       }
       this.callbacks = this.errbacks = null;
   },
   errback: function(value) {
       this.state = "finished";
       this.completion = { type: "error", value: value };
       let a = this.errbacks, n = a.length;
       for (let i = 0; i < n; i++) {
           try {
               let eb = a[i];
               eb(value);
           } catch (ignored) { }
       }
       this.callbacks = this.errbacks = null;
   }
};


Translation of deferred function <D>:
-------------------------------------

deferred function <name>(<params>) { <body> }
~~>
function <name>(<params>) {
   let <D> = new IntrinsicDeferred((function* <name>() { <body> }).call(this, arguments));
   return {
       then: <D>.then.bind(<D>),
       cancel: <D>.cancel.bind(<D>)
   };
}


Translation of await expression within deferred function <D>:
-------------------------------------------------------------

await <expr>
~~>
yield <D>.await(<expr>)


Translation of return statement within deferred function <D>:
-------------------------------------------------------------

return <expr>;
~~>
return <expr>;

return;
~~>
return;

======================================================================

Note how the ability to return <expr>; from a generator, the PEP 380 extension written up for harmony:generators, is used by the next-to-last translation rule.


> So AFAICT they do exactly what you're asking -- provide language level support for libraries to take async control flow in new directions, all without shared state, spooky action at a distance, or attempting to "look sync" :)

Right!

John: I know of no way to make async code "look sync" without raising the risk of code writers and reviewers missing the preemption points, resulting in lost invariants (data races). This is the main objection to deep continuations that I gave. Explicit syntax -- yield, await, wait, etc. -- is best. What people most object to in function nests are the rightward indentation and the closure entrainment (leak/bloat) hazard.

Generators and libraries built on them avoid rightward drift and nested closures. I demo'd an example at NodeConf in May -- see http://brendaneich.com/2011/05/mozillas-nodeconf-presentation/.

/be
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20110906/0f7f778a/attachment-0001.html>


More information about the es-discuss mailing list