tc39 / proposal-cancelable-promises

Former home of the now-withdrawn cancelable promises proposal for JavaScript
Other
376 stars 29 forks source link

Don't use "else" for new clause #31

Closed bergus closed 8 years ago

bergus commented 8 years ago

because in Python it has quite the opposite meaning: it runs when there was no exception.

try:
    res = promised()
except Exception as e:
    onReject(e)
else:
    onFulfill(res)

is literally equivalent to

promised().then(onFulfill, onReject)

I see that your proposal does not allow try-catch-else but only try-else (which Python does not have), but I believe it's still a concern and we should try to come up with something more fitting.

domenic commented 8 years ago

I am happy with else as the name. JavaScript and Python are different enough that people should be able to figure out what code they are reading.

nlwillia commented 8 years ago

I'm on board with the "third state" use case and like the thrown Cancel object and more regular syntax proposal, but the semantics of else now feel slightly more opportunistic than necessary to me. I'd like to come at it from the perspective that if there's not an alternative that works both lexically and semantically then it might be better for the language to avoid forcing the matter.

I remarked in another issue how cancellation is like a deferred condition which makes try { ... } else { ... } appealing, but #24 raised the necessary point that introducing a new exit path from the try block could break legacy code that puts compensating logic in the catch block. Lexically, else is great as a handler block keyword since there's no way it will conflict with existing code, but flipping the error case onto it since the cancel case doesn't work out doesn't feel like a first class acknowledgement of all the possible states.

try {
  ...
} catch (e) {
  if (e instanceof Cancel) {
    // handle cancel only
  } else {
    // handle error only
  }
}

vs.

try {
  ...
} cancel (c) {
  // handle cancel only
} error (e) {
  // handle error only
}

vs.

try {
  ...
} else (e) {
  // handle error only
}

The first example will work with just the new thrown Cancel. The second, unfortunately, isn't possible because we can't introduce new keywords arbitrarily. The third is convenient for the case it handles, but it's lopsided and possibly risky by comparison. People who naively start substituting "else" as the new "catch" could open themselves to similar unplanned path risks as omitting thrown Cancels from catch would have.

The latest draft distinguishes between catch as the exception handler and else as the error handler, which is an excellent way of framing it. But "cancel", the third state, is arguably just as much of an "else" to the try as an error is. So, while we're at it, why not try { ... } default { ... } for cancellations? I ask that tongue-in-cheek to make the point that at some point plucking sort-of-related keywords for their sugaring benefit can drift into territory that complicates the language. I'm not saying that else for errors is completely unacceptable...but since this maps onto Promise syntax as well, then the threshold for introduction may very well be higher than if it was just being evaluated for JavaScript.

domenic commented 8 years ago

Reopening since at TC39 it was pointed out that (as long as we are careful and don't allow catch at the same time) we can use any word.

I do not want to discuss adding a cancel-specific handler, as @nlwillia does in his tome; I think handling cancelations should be rare, and since we cannot do third state any more, it doesn't make sense as a primitive. Open a separate issue if you disagree.

Some possibilities (see Wikipedia):

I like except and recover personally. Twitter poll, tell your friends

nlwillia commented 8 years ago

What about error? It emphasizes the distinction between exceptions (all possible throwables) and errors (a specific type of throwable). ex: Bluebird

domenic commented 8 years ago

Yeah, that's a good possibility as well. I'm surprised no other language has included it.

bergus commented 8 years ago

I like except, it also emphasises that cancellation is not exceptional. Regarding the cancel-specific handler, I did open #36.

zenparsing commented 8 years ago

If I understand correctly, is the idea that the new clause should essentially replace "catch" for most if not all exception handling use cases?

domenic commented 8 years ago

Yep.

zenparsing commented 8 years ago

If I introduce cancellation into an existing codebase, does that mean that I need should update catch-sites to use the new clause instead?

domenic commented 8 years ago

Yes. @littledan thought that would be better than automatically allowing the existing program to adapt. I disagree, but it was a hard requirement from him and others on the V8 team.

bergus commented 8 years ago

Depending on whether you want them to catch cancellations, yes. The default (non-updated) is to treat them as exceptions ("Help, something happened so I don't get my results back, I need to do something about that").

zenparsing commented 8 years ago

Would it be true statement to say that the only way to model cancellation, currently, is by rejecting/throwing?

domenic commented 8 years ago

I'm not sure exactly what you mean. You mean, without this proposal? There are other models, e.g. the one Bluebird uses (where cancelation does not trigger .catch() but does trigger .finally(); this even transfers into the syntactic space if you use function*/yield, but not if you use async/await).

bergus commented 8 years ago

@domenic Bluebirds approach could even transfer to the syntactic space of async/await if you consider a conditional return statement to be hidden in every await that is "tagged with" a cancellation token (via await.cancelToken or a similar mechanism).

markcellus commented 8 years ago

we can't introduce new keywords arbitrarily

I hear this a lot but, as far as I know, promises/async processes are really big in javascript and its one of the key differentiators from other (compiled) languages. Because of that, i think its okay for javascript to introduce new keywords in regards to its promises/async functionality because this particular functionality is already different to begin with.

littledan commented 8 years ago

@domenic I don't want to phrase the point of view on catch semantics changing as a "hard requirement from the V8 team" but it's a concern we are raising as participants in the design process. You were previously able to intercept all non-local control flow with catch, and now you wouldn't be. This also affects existing uses of Promises in an analogous way--you can be sure that one of the two callbacks passed to Promise.prototype.then would be reached. I like the compromise of using a different block form to filter cancels.