Minor correction to Domenic's comments in this (interesting) discussion; IEnumerable and IDisposable are separate concepts in C#. Neither IEnumerable or IEnumerator are disposable objects in C#; *however*, if you use 'for each' on an object that yields an enumerator that is *also* disposable, the compiler-generated sugar for the enumeration will dispose it.
This is an important distinction: As described in the discussion thread here, not all enumerables and/or enumerators are disposable. Many of them are not, and have zero cleanup logic, so there's no need to include or call a Dispose method. That sugar support is explicitly there for enumerators that, if disposed explicitly, can release important resources earlier than the garbage collector would (file handles, sockets, etc.) On the other side (enumerator/enumerable authoring), C# solves this rather lazily: You can use try/finally (but not try/catch) around a 'yield' in an enumerator function, allowing you to do basic cleanup - the cleanup logic is moved into the compiler-generated object's Dispose method. This means that if you want cleanup, you opt into it explicitly, and use the same mechanism you use in normal non-enumerator functions. There's one caveat that this causes unexpected disposal behavior in some corner cases (due to how C# initializes generators), but that's more of an issue with their generator implementation than anything else. On Tue, Apr 29, 2014 at 12:17 PM, Domenic Denicola <[email protected]> wrote: > Dave and Andy's responses have me pinging back and forth as to which "side" > I'm on. Both seem convincing. Dave's response especially brought the issue > into focus for me in a way that I think is clear, so let me explain what I > learned from it: > > What we are essentially talking about here are two types of things: > > - Disposable resources > - Iterable resources > > We are talking about both the sync case, and the async case. Andy's > contention is that most sync iterable resources are not disposable, whereas > Dave's is that most async resources are disposable. Both of these positions > seem plausible to me. > > The question then comes, should for-of handle iterable resources only (as it > does today), or should it take care of disposable resources as well (as it > does in e.g. C#)? > > Andy's code example, viz. > > ```js > var file = openFile("foo.txt"); > try { > for (var line of lines(file)) { > if (line == '-- mark --') { > break; > } > } > } finally { > file.close(); > } > ``` > > advocates for this separation, putting the burden on the user to handle the > disposable part, letting for-of focus on the iterable aspect. Dave advocates > that this code become > > ```js > for (var line of lines(openFile("foo.txt"))) { > if (line == '-- mark --') { > break; > } > } > ``` > > and the disposableness be handled automatically, which is certainly more > convenient for the user. > > This second code example, however, hides two kinds of magic: iterability, and > disposability, in the same syntax. > > An alternative would be to introduce a construct specifically to handle > disposability, like C#'s `using`. You could use it generically such that > `using(x) { ... }` becomes `try { ... } finally { x.dispose(); }`. In > particular the example would become > > ```js > using (var file = openFile("foo.txt")) { > for (var line of lines(file)) { > if (line == '-- mark --') { > break; > } > } > } > ``` > > This still isn't all that convenient, of course. And going along with Dave's > argument, it will become especially inconvenient when async iterables, most > of which will be disposable, start appearing. Perhaps this is why C# decided > to include both iterable and disposable functionality in their `foreach`. > > But inconvenience is easily solved via MOAR SUGAR: > > ```js > for (var line using files) { > if (line == '-- mark --') { > break; > } > } > ``` > > I like this approach for a few reasons: > > - It decouples iterability and disposability, giving each distinct syntax > constructs > - Via sugar, it composes them into something just as convenient as if we had > baked both of them into `for`-`of`, while giving you an explicit signal of > what's going on and what the different semantics are. > - It of course avoids any optimization hazards, being opt-in. > - Most importantly, it pushes off this question into ES7, when we can > properly design a counterpart `using` block to build on top of. > > The drawback of this approach is that it doesn't bake in a disposability > protocol into the language. By saying that `for`-`of` will invoke `return()`, > we are essentially saying "if you want a disposable object, use the method > named `return()` to dispose of it. This kind of ecosystem standardization is > a good thing. But on the other hand, if in ES6 this disposability protocol is > only useful for synchronous iterators---which, as Dave admits, are less > likely to represent disposable resources than async ones---then it's unclear > that much is gained. I'd rather give the ecosystem another year or so without > a standard dispose protocol, if it means we avoid making changes to ES6 this > late in the game. > > Anyway, regardless of the specifics of my `using` proposal, I hope that > highlighting the iterability vs. disposability aspects of this conversation > was helpful to people. > _______________________________________________ > es-discuss mailing list > [email protected] > https://mail.mozilla.org/listinfo/es-discuss _______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

