Error-Type Specific try/catch Blocks

# Joseph Groseclose (8 years ago)

I sent this proposal via www.ecma-international.org/memento/contribute_TC39_Royalty_Free_Task_Group.php# yesterday evening. Sharing it here now: benderTheCrime/error

# Claude Pache (8 years ago)

Note that SpiderMonkey has already nonstandard conditional catch clauses:

try {
    // ...
}
catch (e if e instanceof TypeError) {
    // ....
}
# Joe Groseclose (8 years ago)

Ah, that does somewhat throw a wrench in the system. I'm not sure what the committee's policy is on backwards compatibility and non-standard features. I find the SpiderMonkey handling you just brought to my attention to be a little clunky.

Sent from my iPhone

# Joseph Groseclose (8 years ago)

Nevertheless, perhaps the non-standard implementation could work hand-in-hand with the proposal, where SpiderMonkey could support both solutions in parallel for backward compatibility. I think that the proposed solution is more expressive.

# Joseph Groseclose (8 years ago)
# Michał Wadas (8 years ago)

I can't see why would your syntax be superior to SpiderMonkey extension except saving few characters.

Can your syntax support code like this:

async function foo() { try { throw new Error(); } catch(e if isNativeException(e)) { // special handler for DOM exceptions } catch(e if e instanceof (await fancyClassLoader.load('classes/SomeLibrary/UserDefinedError'))) { // load class asynchronously if it wasn't loaded } }

?

# /#!/JoePea (8 years ago)

Perhaps a syntax based on modules:

try {}
catch (TypeError as e) {}

// ...

try {}
catch (MyError as err) {}
# Boris Zbarsky (8 years ago)

On 5/13/16 11:06 AM, Joseph Groseclose wrote:

I sent this proposal via www.ecma-international.org/memento/contribute_TC39_Royalty_Free_Task_Group.php# yesterday evening. Sharing it here now: benderTheCrime/error-type-specific-catch-proposal

I'd like to understand the proposed semantics better, to see what's actually being proposed and how it interacts with a world in which multiple globals exist.

Given this:

try { something(); } catch MyError (e) { // when do I reach here? }

when does control enter the catch block, exactly? Is it when "e instanceof MyError"? Is it when "e.name == MyError.name"? Something else?

# Joe Groseclose (8 years ago)

@JoePea, that is one of the syntaxes I recommend in the proposal.

@michalwadas The primary goal of this I think is to create a standard feature, but I do not see why an expression wrapped in parenthesis ultimately resulting in an error class would not be supported.

Example:

// ...

} catch (await FCL.load(...), await FCL.load(...)) (e) {
// ...

Or something similar for all of the other recommended syntaxes, replacing the declared class instead with an expression (similar to class extension).

Sent from my iPhone

# Michał Wadas (8 years ago)

} catch (await FCL.load(...), await FCL.load(...)) (e) {

Then you have to override comma operator ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator ) in one specific context...

# Joseph Groseclose (8 years ago)

I offered a few syntaxes for this, perhaps the bitwise OR "|" would be better.

# G. Kay Lee (8 years ago)

This is another really far shot - but I'd rather suggest that we should redefine the role of case so that it's no longer just a part of the switch statement but something more generic, as well as introducing a match keyword to enable pattern matching, including the power of matching by type, so that we can achieve the desired effect in a way illustrated in the snippets below:

// plain old switch-case

switch (expression) {
  case value1:
  case value2:
    ...
    break;

  default:
    ...
}

// switch-case with pattern matching; pattern-match by type in this example

switch (expression) match {
  case _: boolean
    ...
    break;

  default:
    ...
}

// try-catch with pattern matching

try {
  ...
} catch (error) match {
  case _: TypeError
  case _: SyntaxError
    ...
    break;

  default:
    ...
}

Just some rough idea; inspired by Scala syntax.

# Isiah Meadows (8 years ago)

Pattern matching has been considered before, and the performance will suck if you consider things like Point(x, y). It's very hard to optimize at runtime, and engines don't have time to optimize that statically.

If you want to consider Clojure as an example of one that does this dynamically, it still works ahead of time. The inheritance chain is known statically, and Clojure uses immutable ADTs primarily, so it can do this very quickly.

(Spoiler alert: I've tried proposing this myself. It didn't work. You can find previous talk by searching esdiscuss.com via Google.)

# G. Kay Lee (8 years ago)

Hmmm, can't find your proposal, mind to share the link directly?

I did however find a comment from Brendan Eich 3 years ago that basically resonated with my idea that refutable patterns and matching is the way to go for conditional catch, and another thread from a year ago that this still is the generally accepted consensus on the topic.

esdiscuss.org/topic/conditional-catch-clause, esdiscuss.org/topic/conditional-catch

So I guess this is just yet another highly requested feature that somehow ended up in a deadlock then got buried into the deep...

# Isiah Meadows (8 years ago)

TL;DR: I really want full pattern matching, but it's a very long and steep road to climb, and no one has gotten it right yet in a dynamic language [1]. None of the implementors will be okay with it until you demonstrate that it's both possible and practical to implement, with minimal memory and time overhead. It has to work as fast as traditional conditionals for anything beyond unapplying. It cannot slow down parsing or compilation more than marginally. It has to also satisfy the community and its needs.

[1]: Clojure's core.match and equivalents in other Lisps don't count, since they're macros, not language primitives.


This one has possibly the most detailed response to the idea (you'll have to start about half way down):

esdiscuss.org/topic/extensible-destructuring-proposal

Also, I will note that there's quite a few valid points in this essay/thread. Not saying this has no chance of making it into the language, but features do have to pay for their complexity.

esdiscuss.org/topic/the-tragedy-of-the-common-lisp-or-why-large-languages-explode-was-revive-let-blocks

Before I begin the rest of this, keep in mind I really want this feature. I may sound like I don't, but it's that even though I'm aware of the many large technical limitations and requirements for it to work, I still want full pattern matching, and I know it's doable.

And pattern matching, even though exceptionally powerful and useful, is exceptionally complex. If you want it, you'll need to see about getting it implemented in Sweet and Babel/some other transpiler first.

Async functions got in because of the immediate ubiquity of co and other promise-based coroutines, and it was easy to implement. Generators got in because lazy iteration was very difficult with several incompatible models, and coroutines didn't exist, although very sorely needed for async work. Promises/A+ was incredibly ubiquitous, and the revealing constructor pattern was increasingly standardized as well, so it was easy to add.

The thing is, pattern matching is up there with generators in terms of complexity, if not more. For efficient matching, it's even more difficult when you know so little at runtime. For similar reasons, I won't support any proposal that relies on proxies, symbols, magic methods, or anything else that can change at runtime - it's less you have to know, and you can immediately throw an error when something is misused (like trying to unapply against an array), much like how engines already make similar assumptions with typed arrays, often used in performance sensitive code.

To summarize, if you want to get pattern matching into JavaScript, you'll have to cover many bases and address several fears and concerns, including avoiding unnecessary comparisons and calls, avoiding unnecessary accesses, and so on. You'll have to explain how this can be figured out in a single quick pass for a compiler, because engines don't have time to do complex work to compile something. Until you can demonstrate that to be doable with good semantics, and what AST format is best for this (with minimal memory), it'll be an uphill climb. That's what implementors are concerned about.

As much as I want this feature, I do know of many of the technical challenges to it.

# Arthur Stolyar (8 years ago)

If something, here seems to be another similar proposal: zenparsing/es

# Arthur Stolyar (8 years ago)

Something wrong with prev link. zenparsing/es

# Isiah Meadows (8 years ago)

Shiny...

# Michał Wadas (8 years ago)

I still can't see why it's superior to standardizing SpiderMonkey non-standard syntax.

try { ...
} catch(e if e instanceof Error) {

}

I think it's rather inferior, because your doesn't allow C#-like exception filters. It saves you 14 characters, but limits your "easy" filters to instanceof.

To quote article on C# exception filters:

For example, let’s say you are writing code to perform some operation

against a dependency, and that dependency will throw an exception with a bool property that tells you whether the operation IsRetryable.

Now, let’s say that as we’re calling this dependency, we want to be able

to retry any error that has IsRetryable == true up to three times, then abandon if it throws a fourth time.

I think it's legitimate use case and why can't we kill two birds with one stone? Moreover, I think it's bad idea to add static typing to language in only one place (catch clauses).