transpiling ES6 generator functions to ES5: what next?

# Ben Newman (11 years ago)

This is my first post here, after years of lurking.

I'm the primary author of facebook/regenerator, a transpiler for ES6 generator functions that was announced on Hacker News about two weeks ago: news.ycombinator.com/item?id=6594207. On the linked announcement page, there's a live editor that you can use to experiment with the transformer (and easily report bugs if you find any).

I've been really encouraged by the uptake (328 stars on GitHub, 21 forks, 9 contributors so far) as well as projects that have begun to use it, such as Alberto Miorin's browserify transform npmjs.org/package/regeneratorify and Nathan Rajlich's wrapper command npmjs.org/package/gnode.

I have two pretty open-ended questions for this group:

  • Given that this tool will become obsolete as more and more engines implement ES6 generator functions, how can we maximize its value in the meantime? Are there grey areas in the draft spec that can be illuminated? Should I spend my time implementing (or getting others to implement) await syntax and/or control-flow libraries that leverage generator syntax?
  • How would you design a system that selectively delivers transpiled code to ES5-capable browsers and native generator code to ES6-capable browsers, so that end users will benefit immediately when they upgrade to a browser with native support for generators?

Looking forward to any guidance or feedback you feel inspired to share.

# David Bruant (11 years ago)

Sorry for the very late response. This is quite an interesting work, thanks for sharing! I'm particularly interested in your test suite which is impressive.

This is making me realize that generators are fully compilable (efficiently from what I can see) into ES5 and makes me wonder if the current generators specificities are worth it. Very specifically, do we really need Generator.prototype [ @@toStringTag ] === "Generator" ? From an author point of view, I don't really see in which situation this information could matter. As a comparison, functions generated after the class syntax do not have an @@toStringTag to "Class". Generators would just be sugar to write iterators (+ .throw)

Le 03/11/2013 21:55, Ben Newman a écrit :

  • Given that this tool will become obsolete as more and more engines implement ES6 generator functions, how can we maximize its value in the meantime? Are there grey areas in the draft spec that can be illuminated? Should I spend my time implementing (or getting others to implement) await syntax and/or control-flow libraries that leverage generator syntax?

You can most certainly experiment with await syntax and share what you've learned. Are there any test cases that you've written and you feel like the expected spec behavior is odd or unintuitive in some aspect?

  • How would you design a system that selectively delivers transpiled code to ES5-capable browsers and native generator code to ES6-capable browsers, so that end users will benefit immediately when they upgrade to a browser with native support for generators?

Since there is no semantic difference between the ES6 and your compiled version, it's unlikely the users will see a difference at all (not even sure the perf is that much different).

But if you really want to try there are different options with different downsides.

  1. Server-side UA sniffing. You get the User-Agent header, infer which browser it is and decide which version you should be sending. Send the ES5 version when you don't know the UA (safe default)

Downsides:

  • if a browser changes its header, you may be sending the wrong version. This is a problem when you're sending the ES6 version to a non-ES6 browser (which admittedly should be a very rare case)
  • You need to update the list of ES6 User-Agent strings as new browsers arrive
  1. Send a feature-detection JS snippet on the client which will decide which version to load.

Downside:

  • having to wait until this snippet is executed to start code download (or one extra round-trip if code was originally inlined)
  1. send compiler to the client-side

Downside:

  • more code

Personally, I'd go for sending the ES5 version to everyone. My second choice would be 1, but I guess it depends on the requirements.

# Ron Buckton (11 years ago)

I haven't had the opportunity to look at the transpiler, but I did something similar in a fork of TypeScript 0.8.3 on CodePlex a little over a year ago (back when StopIteration was part of the spec.) I've been meaning to update it to the current version, so I'll run it through the test suite below when its ready.

Sent from my Windows Phone

# Brendan Eich (11 years ago)

David Bruant wrote:

This is making me realize that generators are fully compilable (efficiently from what I can see) into ES5 and makes me wonder if the current generators specificities are worth it. Very specifically, do we really need Generator.prototype [ @@toStringTag ] === "Generator" ? From an author point of view, I don't really see in which situation this information could matter. As a comparison, functions generated after the class syntax do not have an @@toStringTag to "Class". Generators would just be sugar to write iterators (+ .throw)

The answer is that class constructor is a Function instance, not a ClassFunction instance, because classes are mostly sugar for the prototypal pattern, whereas generators do not desugar in any translating-not-compiling sense.

Matthias Felleisen wrote a paper, "On the Expressive Power of Programming Languages", that gets at the difference between compilation in general and translation or desugaring. You say generators are sugar to write iterators (+ .throw), but my understanding per Felleisen is that's an abuse of "sugar". Regenerator is a compiler, not a translator of like-to-like-expressiveness languages.

# Ben Newman (11 years ago)

My experience implementing Regenerator is consistent with the nuance that Brendan is highlighting here, as I understand it.

The state machines that Regenerator produces behave to the outside world almost exactly as generators are supposed to behave, but there are gaps: as an example, direct eval inside a generator would have access to state-machine specific state (i.e. the $ctx variable), because some of the machinery that V8 or SpiderMonkey are able to keep hidden simply has to operate in the realm of end-user source code when you don't have native support for generators.

Now, I happen to think it's not that hard to keep those gaps from interfering with actual coding. The fact that facebook/regenerator/blob/master/test/tests.es6.jspasses without translation in Node v0.11.2+ is a testament to how close the semantics are. But in order for a transformation to be considered "desugaring," I agree with Brendan that there have to be no observable gaps in semantics.

If you're building a library based on generators, I definitely recommend writing your code in the most agnostic way you can—for instance, accept any object that has a .next and .throw method as a generator, rather than requiring it to be instanceof Generator, since that's not a built-in constructor in ES5 environments.