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

> On Tue, Sep 6, 2011 at 12:37 PM, John J Barton <johnjbar...@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
_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to