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. What follow is an attempt to sketch the thought
process that lead to that conclusion:
On Sep 9, 2012, at 11:32 PM, Brendan Eich wrote:
>
> function dataSnapshot(aCollection) {
> let snapshot = aCollection.clone();
> return function*() {
> for (let i = 0; i < snapshot.length; i++){
> yield snapshot[i];
> }
> }();
> }
>
Issues:
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.
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
}
}();
}
}
Generator function definitions need to be called to create an actual iterator
object (a generator instance)
It is likely that a common error will be forgetting the invocation of the
generator in return statements such as the above.
There is a potential for confusion about the evaluation time of generator
parameter default value expression. If a parameterized generator needs default
value initialization it is probably better practice to accomplish that via a
wrapper factory:
function IteratorCol(col = CollectionManager.default()) {
return function*() {for (let i = 0; i < col.length; i++) yield col[i]}();
}
is argubaly clearer and more likely to be correct than
function *IteratorCol(col = CollectionManager.default()) {
for (let i = 0; i < col.length; i++) yield col[i];
}
But the wrapper form is less concise than the unwrappered form.
Concise method declarations for generators are misleading about the interface
of the containing class/object because they place emphasis on an implementation
detail (use of a generator to provide an Iterator implementation) rather than
the more important fact that the method returns a object that can be used as an
iterator.
A different approach:
eliminate formal parameters from generator definitions -- if arguments are
needed use a wrapper function/method
eliminate the need to call a generator before before using it as an iterator.
Generator definitions create generator instances rather than generator
constructor functions.
use lexical this/super binding within generator definitions
base generator definition syntax off of arrow function syntax rather than
function definition syntax
eliminate concise generator methods
Examples:
function dataSnapshot(aCollection) {
let snapshot = aCollection.clone();
return *=>{
for (let i = 0; i < snapshot.length; i++){
yield snapshot[i];
}
};
}
or more concisely:
function dataSnapshot(aCollection) {
let snapshot = aCollection.clone();
return *=>for (let i = 0; i < snapshot.length; i++) yield snapshot[i];
}
as a method:
class MyClass {
dataSnapshot(aCollection = CollectionManager.default()) {
let snapshot = aCollection.clone();
return *=>for (let i = 0; i < this.length; i++) yield this[i];
}
}
Proposal summary:
Generator literals -- Base generators syntax on arrow functions rather than
function declarations/expressions
lexical this/super
expression or curly body
Generator literals don't have formal parameters
Generators aren't called -- generator literals create generator instances (a
kind of iterator) when evaluated
No concise generator methods -- use a concise method returning a generator
literal
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss