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