a weird yield* edge case

Allen Wirfs-Brock allen at wirfs-brock.com
Fri Jan 30 19:28:50 PST 2015

Generally, we want yield* to be as transparent as possible, so that if we have a Iterable Inner and a generator defined like:

function *wrapper() {
   yield *Inner();

any sequence of next/throw/return method calls on an iterator produced by wrapper will generate the same results as if  the same sequence of method calls had been directly applied to the Inner iterator instance that is wrapped.

And this generally works (or at least will after I finish fixing some bugs) if wrapper and Inner are both generators.  But here is the edge case where it appears impossible to achieve full transparency.

Generators always[*] have both a "return" and "throw" method and everything works transparently as expected.  However, if the iterator returned by Inner is not a generator object, it might not have a "throw" method but still have a "return" method. Normally a "throw" to such a wrapper iterator would be forwarded by yield* to the inner iterator's throw method, presumably triggering any "finalization" processing needed by the inner iterator.   However, yield* can't forward the the throw to a non-existent inner "throw" method. But the existence of a "return" method is a strong hint that the inner iterator may have some "finalization" it should be given an opportunity to perform. 

It seems like, there are two plausible semantics for this case, each with an undesirable characteristic:

1) yield* doesn't invoke anything on the inner iterator in this situation.  Transparency is preserved, but the inner iterator may have "finalization" processing that never gets executed
2) yield* invokes "return" on the inner iterator in this situation.  Inner iterator gets a chance to run its "finalization" processing.  But full transparency is lost as a method is invoked on the inner iterator that that would not have been invoked with the wrapper wasn't sitting in the middle.

(Note that wrapper already isn't truly transparent, because it is exposing a "throw" method that doesn't exist on the iterator iterator.  It's possible, that the  visibility of the "throw" method is what caused the consumer to invoke it, rather than invoking the "return" method.)

So, opinions on which of these alternatives is better? Are there any others?


[*] this situation actually can occur when Inner is also a generator, but it requires over-riding the 'throw' method for that generator's instance with undefined.

More information about the es-discuss mailing list