On Jul 23, 2014, at 11:12 AM, Brendan Eich wrote:

> Allen Wirfs-Brock wrote:
>> Now that we have return() it isn't clear to me that we actually need throw() 
>> or whether for-of/yield* should call throw() like they currently do.
> 
> Wait, throw is useful anyway, for exception-throwing apart from returning. 
> Right?
> 
> Also I do not know what you mean by "like they currently do".
> 
> I hope this is all pre-caffeine confusion on my part!
> 
> /be
> 

Here's what I'm trying to say:

for (let each of aGenerator) {
     return 42;
}

I think we all agree, that the loop implicitly show do a:
   aGenerator.return(42);  //or perhaps just: aGenerator.return() ??

but what about:

for (let each of aGenerator) {
     throw new Error;
}

Do we do:
  aGenerator.throw(theErrorExceptionObject);  //propagate all unhanded 
exceptions that terminated the loop to the generator
or
  aGenerator.return(); //unwind the generator because the loop is abnormally 
terminating, then propagate the exception

The current spec. draft does the first of these, but I'm not totally convinced 
that is the better alternative.

To me, it seems to come down to how yo think about the generator in 
relationship to its clients.

If you think about it from the perspective of the generator, then one model is 
that a |yield| is really just a callback into the client loop and the generator 
would expect its exception handlers to catch unhanded exceptions generated 
within the callback. 

If you think about it from the perspective of the loop, the generator is just 
an (iterator) object on which you are performing 'next' method calls.  There is 
no particular reason that you would expect an exception occurring in the loop 
body (or from the loop mechanism) to propagate to the iterator object (or any 
other known object) as it isn't in the current call chain.  But if the loop 
abnormally terminates by an unhandled exception you still want to "close" the 
iterator by invoking its 'return' method.

yield* is similar to a loop, in that (as currently specified) it propagates a 
throw() delivered to the outer generator to the inner iterator. Does this 
really make sense?  What relevance is that exception to the inner iterator?  
Why shouldn't yield * just call return() on the inner iterator to "close" it?

So going back to my original questions.  Given that we now have return() 
available to close an abnormally discarded generator, when would somebody 
(presumably implementing a control abstraction) actually want to apply throw() 
rather than return() to an iterator?

catch and finally are really two quite different things.  Catch is a 
dynamically scoped mechanism for finding exception handlers.  Finally, is a 
unwind mechanism for cleaning up local invariants when a function activation is 
abnormally terminated. It's clear, to me, why a control abstraction would want 
to unwind a generator (ie return()) it is discarding.  It's less clear (at 
least seemingly a lot less common) for a control abstraction to want to splice 
into the exception handling of a generator).

I guess what I need to see are some motivating user cases for generator throw() 
where throw is really what is needed rather than return();

Finally, when thinking about these issue if find that 'close' does seem to be a 
more appropriate name than 'return'.  The mechanism is based upon using a 
'return' completion value, but the callers intent seems to be 'close the 
iterator'  (or unwind the iterator).   

Also, I find some of these questions seem simpler to reason about I think in 
terms of an iterator with 'next', 'throw' and 'return' methods rather than a 
generator and its various internal states.  

Allen
_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to