On May 13, 2013, at 11:08 AM, Andreas Rossberg wrote:

> On 13 May 2013 19:24, Allen Wirfs-Brock <[email protected]> wrote:
>> On May 13, 2013, at 2:07 AM, Andreas Rossberg wrote:
>> [...]
>> Yes, it's a very good idea.  The easy way for an imperative programmer 
>> (there are a few of us in the world) to understand yield* is as a yielding 
>> loop over an iterator.  Very straight forward to understand rather than 
>> describing it as a "mechanism for composing generators" (from the wiki) 
>> which I had no idea what it meant until I carefully studied the desugaring.  
>> At that point it because clear that it was just a yielding loop over an 
>> iterator  that for some reason was arbitrarily being restricted to being a 
>> generator.
>> 
>> That restriction is what is breaking the iterator abstraction.
> 
> See my previous reply. I think there is some confusion here about the
> direction of the abstraction.

Likewise, but I won't argue it again as I think we are converging upon a fine 
solution.

> 
>> Could you clarify the special-case handling you have in mind? There is 
>> nothing in the wiki proposal desugaring of yield* that guarantees  that the 
>> delegated object is an actual generator.  All it requires is a "send" method 
>> (and, in that desugaring,  "close" plus "throw" if "throw" is actually 
>> invoked).
> 
> Exactly. But iterators don't currently have 'send' and 'throw'
> methods. So you would want to do something different for
> generator-like objects then you'd do for other iterators.
> 
Or align the interfaces so that the differences don't exist for the situations 
when the two abstractions  are reasonable alternatives.

> 
>> Regarding recursive "sends" to an outer generator.  This shouldn't work, 
>> according to the wiki proposal.  When executing a yield* the outer generator 
>> must be in the "executing" state. Invoking an inner generator from an yield* 
>> via a "send" invocation still leaves the outer generator in the "executing" 
>> state.  If the inner generator invokes "send" on the outer generator the 
>> "send" will throw because the outer is already in the "executing" state.
> 
> The case I was talking about is simply this:
> 
>  function* g() {
>    yield* [1, 2]
>  }
> 
>  var o = g()
>  o.send(undefined)
>  o.send(5)  // what does this mean?
> 
> But I suppose the answer is that the sent value is just dropped on the
> floor, as per the iterator expression interpretation you gave in the
> other post. Makes sense, I guess.

Right.  whenever you "send" a value back to a generator you are doing something 
that requires specific and specialized understanding of the "receiver".  If you 
are sending to an arbitrary generator/iterator you should have no expectation 
other than the the value is likely to be dropped on the floor,. 

>> First why do we need "send" at all.  Why not simply allow an argument to be 
>> passed to "next"  (of course, it is already allowed) and leave it up to the 
>> generator implementation as to whether or not they pay any attention to it.  
>>  Clearly a client needs to be aware when they are using a generator that 
>> expects to receive a value back from yield so that fact must be documented 
>> in the public contract of that generator.  Once that is done, the client can 
>> use "next" as easily as they could use "send".  Of course, if people really 
>> like the name "send" we could also provide that method for generators with 
>> the meaning:
>>    send(value) {return this.next(value)}
> 
> I happen to dislike the name 'send' a lot and would rather call it
> 'resume'. ;) But your suggestion of merging it with 'next' sounds
> plausible as well.

+1 (myself, really :-)

> 
>> That leaves only "throw" as an issue.  Personally, I'd just make it part of 
>> the Iterator interface and provide an Iterator abstract class that provides
>>   throw(exception) {throw exception}
>> as the default "throw" implementation so most iterator authors don't even 
>> have to think about it. Short of that, I think having an explicit behavior 
>> check for "throw" in the yield* algorithm is a very small cost (that only 
>> arises if someone  actually invokes the "throw" method on the outer 
>> generator) and would take care of most common situation where "throw" is 
>> likely to be invoked on an iterator..
> 
> That might actually work. If we manage to truly unify iterator and
> generator types then you got me convinced.

Which?  The abstract class or the guarded throw in yield* (or both).

I think if we can unify all of this we will have simplified the language in a 
way that will benefit many users for a long time to come.

Allen

_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to