Generator issue: exceptions while initializing arguments
Allen Wirfs-Brock
allen at wirfs-brock.com
Sun Sep 9 18:06:47 PDT 2012
On Sep 9, 2012, at 3:32 PM, Brendan Eich wrote:
> I wanted to leave a clean example (no var and let mixing, either):
>
> function dataSnapshot(aCollection) {
> let snapshot = aCollection.clone();
> let i = 0;
> return {
> next: function () {
> if (i == snapshot.length)
> throw StopIteration;
> return snapshot[i++];
> }
> };
> }
>
> (I usually prefer post-increment for loop-ish constructs. Old C hacker here.)
>
> Again, anyone trying to avoid the .clone() call would be disappointed. Anyone trying to avoid the extra level of function nesting would be disappointed. There is irreducible complexity here.
>
> But the generator form is still winning:
>
> function dataSnapshot(aCollection) {
> let snapshot = aCollection.clone();
> return function*() {
> for (let i = 0; i < snapshot.length; i++){
> yield snapshot[i];
> }
> }();
> }
>
What's going on here seems clearer to me, if I think about a generator as a unusual kind of constructor rather than an unusual kind of function that can be suspended and resumed. From that perspective a call to the generator is really a "constructor called as a function" that implicitly does a new, much like several other built-in constructors. Thinking about it that way, you might alternatively write your dataSnapshot function as:
function dataSnapshot(aCollection) {
let snapshot = aCollection.clone();
return new function*() {
for (let i = 0; i < snapshot.length; i++){
yield snapshot[i];
}
};
}
Is new'ing generators intended to be legal?
Instances of generators are iterator objects that implement a state machine based upon the code provided as the body of the constructor. I shouldn't be thinking about the call to the generator as returning a suspended function activation, instead I should be think of it as simply return an object that implements the iterator interface.
My concern about concise generator method syntax is that it makes the generator-ness of the method appear to be an important part of the class instance interface when it should really be an implementation detail. Consider as class such as:
class DataCollection extends Collection {
*@iterator() {
... //implementation details
}
}
The interface of this class should be described as having an @iterator method that returns an object that implements the abstract iterator interface. Whether that object is an instance of a generator or a non-generator based iterator object shouldn't be relevant to clients of this class. It's an implementation detail that can be subject to change within impacting clients. However, the appearance of * as the first character of the method definition gives it an unjustified importance. I might actually prefer the above written as:
class DataCollection extends Collection {
@iterator() {
return new function*() {
... //implementation details
}
}
}
Allen
More information about the es-discuss
mailing list