On Fri 25 Apr 2014 16:22, Domenic Denicola <[email protected]> writes:
>> (2) not well-motivated according to some participants of the > discussion (e.g., it's not necessarily a good idea to rely on > finally-blocks for scarce resource management in the first place, since > they provide only weak guarantees either way). > > This to me is the most worrying point. I feel like Andy summed things up well > in this old thread: > > http://esdiscuss.org/topic/yield-desugaring > > I do not feel that his arguments were ever rebutted. So in the meeting notes the consensus was to send the issue to "generator champions" -- brendan and david herman, and somehow I ended up on CC. We had some backs and forths but as it seems that TC39 members are choosing to discuss this issue here, I'll repost my initial note here. Please read the meeting notes for a description of Jafar's use case. I'm a bit grumpy that this is being brought up again, and this late, and in multiple forums, but as it seems that people want to talk about it again, that talking about it again is the thing to do... * * * If I may summarize Jafar's argument, it's that the iterator in a for-of may hold a scarce resource, like a file descriptor, and because of that, for-of should be able to release this scarce resource on an early exit via "break". The provisional consensus elaborates a method to do this. Is this a fair summary? I sympathise with Jafar's plight but I think that the current setup is the best we can do. The summary of my argument is this: (1) calling return() on iterators is rarely appropriate; (2) return() in generators is semantically weird; and (3) making for-of call return() on early exit is expensive at run-time. I should note first that this situation is not limited to generators, so the starting point mention of "finally" blocks is something of a distraction. To the extent that this issue applies to generators, it also applies to other kinds of iterators. Indeed I expect that in practice most iterators in an ES6 program will be map, set, and array iterators, which in practice will not be implemented with generators. Incidentally I think that if TC39 decided to re-add this method, it should be called close() instead, because it doesn't make sense to "return" from a non-generator iterator. == Calling return() on iterators is rarely appropriate Again I do sympathise with the use case, but we should start with a discussion of what is the common case. If we knew that the @@iterator call in the for-of would return a fresh iterator, then it would make more sense to provide some means for closing on early exit. Jafar argues that this is in fact the common case, which sounds about right to me. However, holding a scarce resource is also likely to be uncommon. It certainly doesn't come up in the browser, for example. I think it's reasonable in that rare case to require some thought on the part of the user as to what scarce resources they have acquired, and to arrange to release them as appropriate. Granted, if you are a user of an iterator, you might not know that it has a scarce resource. So there are two cases here: one in which the iterator was created by its consumer, and one in which the consumer is decoupled from the producer. The first case is the one Jafar gives in his notes: for (var line of openFile("foo.txt")) if (line == '-- mark --') break; However in this case it is possible to arrange to close the iterator, with a different interface: var file = openFile("foo.txt"); try { for (var line of lines(file)) if (line == '-- mark --') break; } finally { file.close(); } Among other possibilities. Something like Python's "with" might be appropriate here. The point is that although in this case, calling return() on the iterator may indeed be appropriate, the desired behavior can still be implemented. Note that there is nothing special about for-of or iterators in this example; any abstraction that captures a scarce resource has to do the same thing. It is not that generators are unable to abstract over IO -- it is that they are unable to transparently abstract over scarce resource acquisition. No surprise there. The other case is when you have an iterator consumer which is decoupled from the code that created the iterator, as in: function (iterable) { ... for (var x of iterable) { if foo(x) break; } ... } But it is precisely in this case when you would *not* want to close the iterator, because you don't know its lifetime. == return() in generators is semantically weird I know the argument has already been made, but I would like to repeat my point (2) from https://mail.mozilla.org/pipermail/es-discuss/2013-May/030683.html, namely that close() "complicates the mental model of what happens when you yield." It's really strange to consider a yield as not only an expression that produces a value, or possibly a point at which an exception could be thrown, but also a "return". Bizarre. It's a hazard to reading generator functions. Also, the insistence on a return() that doesn't run catch blocks seems to me to be ill-placed. I think it's telling that the counter-examples are from Python, which has a different semantic model, as it has finalization. Implementing abstractions over scarce resources in JS is going to necessarily involve different design patterns than those used by Python. For the given use-case, throw() is entirely sufficient. If you don't trust your generators to do the right thing on an exception, you shouldn't be acquiring scarce resources! Finally, the given use-case is incompletely specified; a loop can exit prematurely through exceptions as well as through "break". So really what is proposed is a finally block in every for-of statement, which brings me to my next point... == Calling return() on early exit from for-of is expensive Wrapping a try/finally around each for-of is going to be really expensive in all engines right now. I'm skeptical about our ability to optimize this one away. Avoiding try/catch around for-of was one reason to move away from StopIteration, and it would be a pity to re-impose this cost on every for-of because of what is, in the end, an uncommon use case. I think the expected result of doing this would be performance lore to recommend using other iteration syntaxen instead of for-of. There's no perfect answer when it comes to abstractions over scarce resources. Given the constraints of what JS is, its finalization model, its deployment in the browser, and its engines, for me the status quo is the best we can do. I know that for people that open file descriptors, that's somewhat unsatisfying, but perhaps such a cross is what goes with the crown of being a true Unix hacker ;) Regards, Andy _______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

