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