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