revisiting "shift"

David Herman dherman at
Wed Apr 28 08:50:07 PDT 2010

> What happens if you don't supply a function but another type, or none?

The simplest thing is to specify it as a runtime error if the argument to shift is not callable. You're right that there's an overhead to constructing a new function. But it gives you flexibility that's otherwise a pain for the programmer. More below.

> Would 42 still be "returned" by shift? Or is it actually the returned value in k, that gets a send method augmented to it?

I don't understand this question-- do you mean whatever value the handler function (in the example, function(k) { return k }) returns? Then no, there's no augmentation or mutation here. The continuation is represented as an object with three methods:

- send(v): pushes the suspended activation back onto the stack and uses v as the result of the shift expression

- throw(v): pushes the suspended activation and throws v from the shift expression

- close(): pushes the suspended activation and performs a return (running any relevant finally blocks first)

(This is all just what JS 1.7 generators do.)

A simpler representation for captured continuations is just a function. But as Kris pointed out in an earlier thread, this is inconvenient for the "throw" and "close" cases.

> It'd be cleaner if it was just shift()

You might think so, since the semantics seems simpler-- but it would lead to uglier programs. You're not affording the writer of the function doing the shift any ability to specify what to do after the shift, and you're not giving them the ability to communicate any data to the caller. This requires them to coordinate with clients to save any additional action in some side-channel, e.g.:

    // library
    function gen(thenWhat) {
        thenWhat.action = function() { ... /* do more stuff */ ... };
        let received = shift();

    // client
    var k = gen({ action: function() { } });

> But maybe I'm knifing an entire API here I don't know about :) Otherwise the send method seems redundant.

I'm not sure what the send method has to do with it-- it sounds like I may not have explained clearly enough. The semantics of shift is to capture and pop the current activation, reify it as an object with the three methods I describe above (send, throw, close) and call the handler function with this reified activation object as its argument. It's then up to the program to decide when/whether to continue the captured function by calling its methods.

Note that this means that when you use the shift operator, the handler function is executed immediately, whereas the rest of the captured function is suspended and not continued until some later time. This is the opposite of patterns like event callbacks and CPS, where the code in the callback is called at some later time, but the rest of the current function is continued immediately.


More information about the es-discuss mailing list