tc39 / proposal-cancelable-promises

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

`throw cancel` syntax does not work #12

Closed domenic closed 8 years ago

domenic commented 8 years ago

throw cancel (foo) already has a meaning, equivalent to const x = cancel(foo); throw x;. New syntax is needed.

I don't have any ideas I currently love, but some committee members have suggested throw.cancel, which might be a fallback.

I'd like to discourage idle suggestions from people who do not have experience in validating JavaScript grammar productions against the spec and against implementation constraints. Just throwing out your idea of what would be nice syntax is not a productive way to work in this repo.

benjamingr commented 8 years ago

throw.cancel sounds grammatically viable but it feels strange - much stranger than things like new.target which are strange on their own.

Other alternatives can include an operator - something like throw@ e but that's likely worse.

bterlson commented 8 years ago

In discussing the STC proposal, we came up with the idea of return.tail. This was rejected by some on the grounds that the special dot-keywords should be exclusively value-producing expressions (ie. identifier-like).

domenic commented 8 years ago

Here is what I have come up with as potential alternatives:

{break, continue, return} are nice control-flow keywords, but already have meaning. I'm not sure adding "throw" before/after them is enough to avoid confusion with their existing meanings, all of which stop at loop or function boundaries.

throw* by itself seems a bit dangerous, especially if you pair it with catch*. It moves away from the semantic being "cancelation" and more toward it being "throw something special" with a non-obvious part of that specialness being that it doesn't cause HostReportErrors.

One problem with things like throw* cancel is that I am unsure whether its counterpart is catch* cancel (e) { ... } or catch cancel (e) { ... }.

I am also vaguely interested in throw return and return*, in the spirit of "nonlocal return". I think the counterparts are catch return (e) { ... } and catch return* (e) { ... }. However, these have the possibility of shifting the entire language to "return", e.g. "return tokens", Promise.return(v), etc., which is kind of unfortunate. We could try to say that the concept is cancelation and the syntax just happens to use the word return. Maybe that is OK since we already have the mismatch of Promise.reject(r) + promise.catch(...).

For now I will tentatively proceed with:

But this subject is definitely not closed...

inikulin commented 8 years ago

Looks a little bit confusing, how about:

cancel&throw new CancellationReason('Yo!');
domenic commented 8 years ago

Ehhhhhh meh. Also BTW the CancelReason class was shot down by TC39; we should just use strings.

inikulin commented 8 years ago

Anyway, it's introduces ambiguity:

foo&bar&cancel&throw new CancellationReason('Yo!'); 
benjamingr commented 8 years ago

Also BTW the CancelReason class was shot down by TC39; we should just use strings.

I think this is problematic - strings are value types and as such properties can't be attached to them. This means no .stack and no ability to know where the cancellation originated which might be useful when debugging.

domenic commented 8 years ago

You can use objects if you want; it's not syntactically enforced or anything. But it was definitely a point of agreement that stacks should not be collected, as this is not an exceptional situation and the overhead of stack-tracking is not something we want to incur. That was one of the primary arguments in favor of a separate third state, instead of a special exception type.

benjamingr commented 8 years ago

@domenic I think the strong case for a separate third state is that it makes a lot of sense conceptually since you're not signalling completion or an error condition but actually something else.

I'm in complete agreement that we need cancellation semantics for async functions. and other things to come. I don't understand why stack traces wouldn't be useful - they wouldn't be as useful but if my code cancelled something and my app is stuck I sure probably want to know why. There is a whole class of tooling problems we're not even aware of yet.

domenic commented 8 years ago

I'll split it off into another issue.

domenic commented 8 years ago

Anyway, it's introduces ambiguity:

I don't believe that's the case, as throw can only be used in statement position, not expression position.

domenic commented 8 years ago

cancel catch and cancel throw (with [NoLineTerminatorHere]) works, and so I've updated the current draft to have that.