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

Reply via email to