Questions/issues regarding generators

Andreas Rossberg rossberg at google.com
Mon Mar 18 06:30:55 PDT 2013


On 16 March 2013 07:34, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
>
> On Mar 15, 2013, at 5:55 PM, Waldemar Horwat wrote:
>
>> On 03/14/2013 04:14 PM, Brendan Eich wrote:
>>> Andreas Rossberg wrote:
>>>> On 14 March 2013 23:38, Brendan Eich<brendan at mozilla.com>  wrote:
>>>>> Andreas Rossberg wrote:
>>>>>> That leaves my original proposal not to have generator application
>>>>>> return an iterator, but only an iterable. Under that proposal, your
>>>>>> example requires disambiguation by inserting the intended call(s) to
>>>>>> .iterator in the right place(s).
>>>>> That's horribly inconvenient. It takes Dmitry's example:
>>>>>
>>>>>  function* enum(from, to) { for (let i = from; i<= to; ++i) yield i }
>>>>>
>>>>>  let rangeAsGenerator = enum(1, 4)
>>>>>  let dup = zip(rangeAsGenerator, rangeAsGenerator)  // Oops!
>>>>>
>>>>> which contains a bug under the Harmony proposal, to this:
>>>>>
>>>>>  function* enum(from, to) { for (let i = from; i<= to; ++i) yield i }
>>>>>
>>>>>  let rangeAsGenerator = enum(1, 4)
>>>>>  let dup = zip(rangeAsGenerator[@iterator](), rangeAsGenerator[@iterator]())
>>>>
>>>> No, why? The zip function invokes the iterator method for you.
>>>
>>> Sure, but only if you know that. I thought you were advocating explicit iterator calls.

I _have_ to know that, either way, because that is a fundamental part
of the function's contract. Its interface can be sketched as

  zip : (iterable, iterable) -> iterable

That implies that it takes care of invoking the iterator method of its argument.


>>> A call expression cannot be assumed to return a result that can be consumed by some mutating protocol twice, in general. Why should generator functions be special?
>>>
>>> I agree they could be special-cased, but doing so requires an extra allocation (the generator-iterable that's returned).
>>>
>>> Meanwhile the Pythonic pattern is well-understood, works fine, and (contra Dmitry's speculation) does not depend on class-y OOP in Python.
>>>
>>> I guess it's the season of extra allocations, but still: in general when I consume foo() via something that mutates its return value, I do not expect to be able to treat foo() as referentially transparent. Not in JS!
>>
>> Does for-of accept only iterables, only iterators, or both?  Presumably a function like zip would make a similar decision.  The problem is if the answer is "both".
>
> Strictly speaking for-of only accepts iterables since it always invokes the @@iterator on the value to the RHS of the 'of' keyword.  For-of expects to get a iterator back from the @@iterator call.
>
> Using informal interface descriptions this can be described as:
>
> interface <iterable> {
>    @@iterator: ()-> <iterator>
> }
>
> interface <iterator> {
>    next: () ->  <nextResult>
> }
>
> interface <nexdtResult> {
>    done: <boolean>,
>    value: <any>
> }
>
> class Array implements <iterable> {...}
> class Map implements <iterable> {...}
> class Uint32Array implements <iterable> {...}
> etc.
>
> class "builtinArrayIterator" implements < iterator >+< iterable > {...}
> class "builtinMapIterator" implements < iterator >+< iterable > {...}
>
> The dual nature of the built-in iterators is nice because it allows formulation like:
>
> for (v of myArray) ...  //iterate over an array using its default iterator
> for ([k,v] of myArray.entries())  ... //iterate over an array using an alternative iterator as the iterable
> for (ev of (for (v of myArray) if (v&1==0) v)) ...  //the iterable is a generator expression

None of these examples requires built-in iterators to also be
iterables, or generator results to also be iterators. For-of always
invokes @@iterator, so it's sufficient here if generator objects and
collections are plain iterables.


> My understanding of Andrea's concerns is that there is a hazard that somebody might try to invoke @@iterator more than  once on an iterator that was also an iterable.

My concern is that I _want_ to be able to invoke @@iterator more than
once _on any iterable_. Otherwise iterable-based abstractions like zip
_will not work_ with all (combinations of) iterables. Which would be a
real shame.

In other words, iterables should always be proper factories for
iterators (at least the ones provided by the language, obviously we
cannot make any guarantees whether user-defined ones properly
implement that contract).

Generator objects, as currently drafted, don't meet that goal (Python
precedence notwithstanding). In general, no object simultaneously
trying to be both an iterator and an iterable (returning itself)
sanely can. You don't get a duality but an inconsistency.

/Andreas


More information about the es-discuss mailing list