Single frame continuations proposal

Dave Herman dherman at mozilla.com
Thu Apr 1 08:45:21 PDT 2010


> I am not exactly sure what that intent is here, but I am guessing it
> is supposed to flash several times before showing the alert.

No, sorry that I meant window.setInterval rather than window.setTimeout, but otherwise I think I wrote what I meant. I wanted it to alert "done setup!" immediately after installing the animation, not after the animation finishes. Similarly, I wanted setFlashing to add itself to an array immediately after setting up the animation. Don't worry about only doing it 10 times, just let it flash forever. :)

> This really lead me to think about if it would be possible to meet
> the
> goals of this proposal *and* make this fully compatible (syntax and
> default behavior) with JS 1.7 generators. That is make it compatible
> with generators and still making it sufficiently extensible to meet
> the broad range of needs that could utilize single-frame
> continuations/coroutines. And I believe it is, and it could done in a
> way that this proposal could be implemented in JavaScript as well
> generators. Do you think it would be worth exploring that
> possibility?

I actually have already sketched such a translation, albeit using the semantics I proposed. I'm including it below. Essentially, you have to translate *two* things: 1) the `yield' expression form, and 2) generator functions. The latter is necessary because generator functions don't start executing their function body until the first `send'. So the translation is:

    yield <expr> ~~> this.receive(this->yield(<expr>))

and

    function f(x1, ..., xn) body ~~>
    function f(x1, ..., xn) {
        return new Generator(function () {
            (function f(x1, ..., xn) body).call(this, x1, ..., xn);
        });
    }

Actually, I think this probably gets `this' wrong; I don't have the time ATM to look up what the semantics of `this' is within a generator body. But that should be pretty easily fixable.

At any rate, this is relatively clean and I think's a plausibility check that single-frame continuations should be simpler and more general than generators, and likely compatible with them.

> I'll put a sketch together in another email for this alternate
> approach, (if it doesn't sound good, we can revert back to digging
> into this one).

I imagine it should be pretty easy to adapt this translation to work with your semantics instead, but for my sake it'd be more helpful to understand your semantics better first.

Dave

var [StopIteration, Generator] =

(function() {

var NEWBORN = 0;
var DORMANT = 1;
var RUNNING = 2;
var CLOSED = 3;

var StopIteration = {};

function Yield(x) { this.value = x; }
function Send(x) { this.sent = x; }
function Throw(x) { this.thrown = x; }

// [[function f(x1, ..., xn) body]] =
// function f(x1, ..., xn) {
//     return new Generator(function() {
//         (function f(x1, ..., xn) body).call(this, x1, ..., xn);
//     });
// }
function Generator(f) {
    this.state = NEWBORN;
    this.suspended = function(x) {
        if (x !== void(0))
            throw new TypeError("newborn generator");
        // no need to bind `this' (will be this generator)
        return f.call(this);
    };
};

Generator.prototype = {

    // [[yield e]] = this.receive(this->yield(e))
    yield: function(x, k) {
        if ((this.state !== NEWBORN) && (this.state !== RUNNING))
            throw "yield from dormant or dead generator";
        this.state = DORMANT;
        this.suspended = k;
        return new Yield(x);
    },

    receive: function(x) {
        if (x instanceof Send)
            return x.sent;
        else if (x instanceof Throw)
            throw x.thrown;
    },

    sendOrThrow: function(x) {
        switch (this.state) {
        case RUNNING: throw "already running";
        case CLOSED:  throw StopIteration;
        default:
            this.state = RUNNING;
            var result = this.suspended.call(this, x);

            // generator yielded
            if (result instanceof Yield)
                return result.value;
            // generator returned
            else {
                this.state = CLOSED;
                throw StopIteration;
            }
        }
    },

    send: function(x) {
        return this.sendOrThrow(new Send(x));
    },

    next: function() {
        return this.send(void(0));
    },

    throw: function(x) {
        return this.sendOrThrow(new Throw(x));
    },

    close: function() {
        if (this.state === RUNNING)
            throw "already running";
        this.state = CLOSED;
        this.suspended = null;
    }

};

return [StopIteration, Generator];
})();


More information about the es-discuss mailing list