A syntax alternative for generators

# Brendan Eich (13 years ago)

Allen Wirfs-Brock wrote:

The initializing generator arguments thread and related discussions has got me questioning our use of function declaration/expression syntax as our basis for defining generators. Changing this would be a departure from what Firefox has supported in recent years, but in ES6 we have more constructs to build upon than FF did in 2006. The conclusion I reach is that it might be better to build generator definition upon a foundation of arrow functions rather than classic JS function definitions.

I strongly believe we should not go off into uncharted (as in unprototyped and not user-tested, or based on established work in other languages) territory.

There's nothing wrong with using function* (and much right) for generators, compared to alternatives we've already discussed. We can't add a new keyword even with newline sensitivity, and preserve the ability to write generator function expressions. Programmers who know Python don't want a new keyword. And arrows are not binding forms, while generators generally (but not always) want to be function declarations.

  • If a generator needs to capture creation time state it needs to be wrapped in a factory function/method, similar to Brendan's example above.

I'll be blunt here. You are making up something that is not common enough to impair generators in Python or JS1.7+. We have many years of experience with these languages. This contrived or a-priori worry needs evidence to motivate disrupting ES6 plans, at this point.

  • If the factory is a method the wrapping creates confusion about this/super binding. For example:

class MyClass { dataSnapshot(aCollection) { let snapshot = aCollection.clone(); return function*() { for (let i = 0; i < snapshot.length; i++){ yield thls.doSomething(snapshot[i]); //this is not what was probably expected, super would be illegal } }(); } }

That's JS: you have to bind |this| one way or another, just as for any inner function. There is nothing new here.

Indeed trying to make generators different from functions just makes for more non-compositionality. Or assuming this must be the outer |this| just assumes the arrow conclution. Again it is very rare to write a nested generator this way, just to have a prolog that takes a snapshot. Here's a better collection example:

function collectionIterator(aCollection) { for (let i = 0; i < aCollection.length; i++) yield aCollection[i]; }

function snapshot(aMutableCollection) { return collectionIterator(aMutableCollection.clone()); }

By removing the contrived nesting and declaring a reusable collectionIterator, separating concerns, the gravamen of your complaint goes away.

  • Generator function definitions need to be called to create an actual iterator object (a generator instance)

This is a feature, don't break it.

  • It is likely that a common error will be forgetting the invocation of the generator in return statements such as the above.

Contrived code has contrived hypothetical common error. Evidence!

  • There is a potential for confusion about the evaluation time of generator parameter default value expression.

No more than anything in the body, if we use the simple and flat semantics Jason advocated.

I really think this evidence-free approach to overturning the generator design, where we've stood on Python designers' and developers' shoulders and prototyped for almost six years, is bad business. I know, it's "just es-discuss", but at this point we have consensus based on the champions model for ES6. What you are doing is disorderly. It would be less disorderly, but still objectionable, to simply cut generators (and default parameters).

# Brendan Eich (13 years ago)

Contrived example has contrived error. Sorry, this is really not fairly argued or evidence-based.

Generators are the easiest way to write an iterator implementation. Typically iterator implementations are named, reusable. The better way to write anything like the mutable collection snapshot iteration is (from my earlier message):

function collectionIterator(aCollection) { for (let i = 0; i < aCollection.length; i++) yield aCollection[i]; }

function snapshot(aMutableCollection) { return collectionIterator(aMutableCollection.clone()); }

But mutable collections can sometimes have smarter lazily snapshotting iterators, so this is just a sketch. Still this is very much like real JS built on closures and Prototype- and jQuery-like clone/extend utilities.

Prototype in particular is full of this.each(function(...){...}) and Object.keys(...).each(function...) uses. No one writes ad-hoc/non-reusable closure-based iterators, as far as I can see.

s/generator/closure-based/ above makes no difference -- a point I made earlier. You cannot single out generators here any more than closure-based iterator factory functions.