Adam Crabtree (2013-11-20T17:30:55.000Z)
Better error handling in JavaScript is incredibly important. As I see it,
there are 3 fundamental roadblock or obstacles that undermine the utility
of JavaScript's error handling:

1. Lack of typed catch
2. Lack of Error.create (solved from what I understand to some degree by
Object.setPrototypeOf)
3. Lack of optimization of try/catch code (in V8)[1]

I'll only talk about #1, but, briefly, #3 is additionally important because
of its real world ramifications (e.g., node.js' explicit non-use of
try/catch to avoid it[2]). I conjecture fixing #3 has not been made a
higher priority because of the weakness of JavaScript error handling due to
#1 and #2.

Regarding typed catch, typed catch is not just sugar. The above instanceof
check works, but rethrowing obfuscates the reported throw line. node.js
again is an example of a codebase that adapts to this by never attempting a
typed catch to avoid rethrowing.

Async code, control-flow, and async generators all largely owe their
origins in JavaScript to try/catch.

As an aside, node.js error handling is fundamentally broken and
inconsistent and promises violate assumptions by catching errors
originating in core.[3]

Thunks (callback(err, result)) and consequently promises arose out of async
programming in JavaScript and the inability to catch errors from a
different event loop tick.

Because try/catch could not catch errors in another tick, errors could not
be caught and thus should not be thrown, and thus the thunk was born.
Promises are a monadic alternative to callbacks, but not to try/catch
unless you wrap all function calls (even synchronous non-IO) in a promise,
which will slow your execution by separating synchronous execution across
event loop ticks per the spec, effectively a non-starter. CPS is a true
alternative to try/catch/throw, except for the lack of proper tail calls.

So, we have 3 options:
1. Monadic approach (e.g., Maybe)
2. CPS (requiring a trampoline in lieu of PTC)
3. try/catch/throw (requiring workarounds to avoid DEOPT)

There are tradeoffs to each approach. #3 is the language default since the
first two have significant architectural implications, or at least require
opt-in. There is technically a 4th common alternative of never catching. In
node though, this leads to requiring a process restart and DoS liability
since core shares the same stack as userland (application) code and
throwing places it into an undefined state by not allowing the stack to
unwind (they should be catching). I wrote the async trycatch module[4] to
solve this.

*Summary:*
With async generators and the ability to use the default language error
handling construct (try/catch) across async IO, try/catch is the best error
handling mechanism in JavaScript, no longer requiring ugly hacks like my
trycatch module. However, without typed catch, error handling is ugly and
inconvenient, resulting in anti-patterns like the following

try{
  // some code
  JSON.parse(foo)
  // some code
} catch(e) { /* not always what you think it is */ }

that typed catch would easily solve.

Cheers,
Adam Crabtree

[1] https://code.google.com/p/v8/issues/detail?id=1065
[2] https://github.com/joyent/node/issues/5149#issuecomment-15761301
[3]
http://nodejs.org/docs/latest/api/domain.html#domain_warning_don_t_ignore_errors
[4] https://github.com/CrabDude/trycatch


On Wed, Nov 20, 2013 at 8:05 AM, Jeremy Martin <jmar777 at gmail.com> wrote:

> I can't speak for Benjamin, but I would like to "+1" the notion that
> try/catches could benefit from some form of error pattern matching.  Here's
> an example that attempts to avoid conflation with async control-flow
> considerations:
>
> ```javascript
> try {
>     JSON.parse(getJSONString());
> } catch (err) {
>     if (err instanceof SyntaxError) {
>         // handle the error
>     } else {
>         throw err;
>     }
> }
> ```
>
> Discriminatory error handling like this is not all that uncommon, and
> could potentially benefit from an expansion of the existing guards strawman
> [1].  For example:
>
> ```javascript
> try {
>     JSON.parse(getJSONString());
> } catch (err :: SyntaxError) {
>     // handle the error
> }
> ```
>
> This of course has larger implications, though, as you'd presumably need
> to support multiple "guarded" catches, and behavior for a failed guard is
> different in the try/catch scenario than with parameters or properties.
>
> [1] http://wiki.ecmascript.org/doku.php?id=strawman:guards
>
>
> On Wed, Nov 20, 2013 at 10:24 AM, Brendan Eich <brendan at mozilla.com>wrote:
>
>> Benjamin (Inglor) Gruenbaum wrote:
>>
>>> Hi, thanks for the comment and sorry for the unclarity.
>>>
>>> I was refering to my original post in this thread about try/catch with
>>> generators: http://esdiscuss.org/topic/try-catch-conditional-
>>> exceptions-in-light-of-generators#content-0
>>>
>>
>> Yes, but the Domenic pointed out that the reference error there has
>> nothing to do with generators so much as async control flow in general (it
>> could be in a function passed as a callback or downward funarg, and called
>> "later" by any means). And you seemed to agreed.
>>
>> It's true that yield allows part of a function to run in a later event
>> loop turn -- or just in a later iteration of a loop in the same turn.
>> Generators by themselves are not async in the event loop sense. So first
>> you need to identify the problem precisely.
>>
>> If you write
>>
>>   function f() { ... setCallback(function g() {... RefErrHere ... }) ... }
>>
>> you need test coverage over the body of g.
>>
>> If you write
>>
>>   function* f() { ... yield; RefErrHere ... }
>>
>> same deal. There is no difference in kind.
>>
>> So, is the problem you are citing a general one of test coverage over
>> async control flow?
>>
>>
>> /be
>>
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>>
>
>
>
> --
> Jeremy Martin
> 661.312.3853
> http://devsmash.com
> @jmar777
>
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
>


-- 
Better a little with righteousness
       than much gain with injustice.
Proverbs 16:8
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20131120/5b025138/attachment-0001.html>
domenic at domenicdenicola.com (2013-11-29T17:32:31.888Z)
Better error handling in JavaScript is incredibly important. As I see it,
there are 3 fundamental roadblock or obstacles that undermine the utility
of JavaScript's error handling:

1. Lack of typed catch
2. Lack of Error.create (solved from what I understand to some degree by Object.setPrototypeOf)
3. Lack of optimization of try/catch code (in V8)[1]

I'll only talk about #1, but, briefly, #3 is additionally important because
of its real world ramifications (e.g., node.js' explicit non-use of
try/catch to avoid it[2]). I conjecture fixing #3 has not been made a
higher priority because of the weakness of JavaScript error handling due to
.#1 and #2.

Regarding typed catch, typed catch is not just sugar. The above instanceof
check works, but rethrowing obfuscates the reported throw line. node.js
again is an example of a codebase that adapts to this by never attempting a
typed catch to avoid rethrowing.

Async code, control-flow, and async generators all largely owe their
origins in JavaScript to try/catch.

As an aside, node.js error handling is fundamentally broken and
inconsistent and promises violate assumptions by catching errors
originating in core.[3]

Thunks (callback(err, result)) and consequently promises arose out of async
programming in JavaScript and the inability to catch errors from a
different event loop tick.

Because try/catch could not catch errors in another tick, errors could not
be caught and thus should not be thrown, and thus the thunk was born.
Promises are a monadic alternative to callbacks, but not to try/catch
unless you wrap all function calls (even synchronous non-IO) in a promise,
which will slow your execution by separating synchronous execution across
event loop ticks per the spec, effectively a non-starter. CPS is a true
alternative to try/catch/throw, except for the lack of proper tail calls.

So, we have 3 options:

1. Monadic approach (e.g., Maybe)
2. CPS (requiring a trampoline in lieu of PTC)
3. try/catch/throw (requiring workarounds to avoid DEOPT)

There are tradeoffs to each approach. #3 is the language default since the
first two have significant architectural implications, or at least require
opt-in. There is technically a 4th common alternative of never catching. In
node though, this leads to requiring a process restart and DoS liability
since core shares the same stack as userland (application) code and
throwing places it into an undefined state by not allowing the stack to
unwind (they should be catching). I wrote the async trycatch module[4] to
solve this.

*Summary:*

With async generators and the ability to use the default language error
handling construct (try/catch) across async IO, try/catch is the best error
handling mechanism in JavaScript, no longer requiring ugly hacks like my
trycatch module. However, without typed catch, error handling is ugly and
inconvenient, resulting in anti-patterns like the following

```js
try{
  // some code
  JSON.parse(foo)
  // some code
} catch(e) { /* not always what you think it is */ }
```

that typed catch would easily solve.

[1]: https://code.google.com/p/v8/issues/detail?id=1065
[2]: https://github.com/joyent/node/issues/5149#issuecomment-15761301
[3]: http://nodejs.org/docs/latest/api/domain.html#domain_warning_don_t_ignore_errors
[4]: https://github.com/CrabDude/trycatch