The Error type

David Bruant bruant.d at gmail.com
Fri Aug 17 03:35:44 PDT 2012


Interesting topic.

I'd like to share some experience and thoughts which are complementary 
and may influence the proposal.
I've been playing with promises with some time and the way errors are 
taken care offers different opportunities than "normal" error handling.

When using promises (I used the Q library), most errors are now caught 
by the library and you rarely find them in your JS console which leads 
to some frustration especially when playing with APIs a-la-mocha with 
"done" functions you have to call when you're done (because in case of a 
thrown error in a test case, you don't call done and the error is 
absorbed by Q if you don't explicitly log it).
The error forwarding mechanism is however much less violent and 
intrusive than throwing (which unstacks function calls) until being 
caught because the failed promise is a regular value. It is the 
responsibility of those who have access to the promise to decide which 
"channel" they want to listen (the "then" or "fail" "channel"). If you 
don't want to listen to one channel (like the fail channel most often), 
you can keep doing what you want without worrying about it. The next 
piece of code who cares about the fail channel will read what's in it 
and choose to forward or alter the value or "empty" the "fail channel".


Based on this experience I'd like to suggest a slightly different 
approach to the "error type".
Since it's quite a different model (from what exists already and Peter's 
proposal) and may require major rewrite of existing engines (because it 
deals with JS value representation), I'm putting it as a thought 
exercise and food for thought for (compile-to-JS?) language designers.
The way I see it, conceptually each value would have 2 slots, one for 
the "normal flow" case and one for the "error flow" case. Syntax 
constructs would decide which slot is being used. In most cases, it 
would be the "normal flow slot" (like with promises where most of the 
time, one write the .then without the .fail).
Because of the 2 slots, there would be no need for a new error type, it 
would be a part of all types somehow.

If considered to be applied to JS, everything could be considered to 
currently work with just normal flow values. However, for error cases 
(like indexOf returning -1), the error slot could be filled in and some 
new syntax/functions could help make more clear that we're treating the 
error case (and not caring about the exact -1 value or empty string or 
whatever the "normal flow" value has been decided to be). The good side 
of 2-slots values is that errors can be associated with any value of any 
type.
Values thrown would have both their normal and error flow values to the 
same value.
It's likely that engines could optimize by optimistically consider being 
in the "normal flow" most of the time and there would be a negligible 
performance overhead if error slots aren't abused I think (but it'd be a 
major rewrite anyway I think).

Could what I just described implemented with "value proxies"? I'm not 
very familiar with the proposal.

Anyway, as I said, food for thought, not an actual idea to be added to JS.

David


Le 16/08/2012 20:56, Peter van der Zee a écrit :
> I was jesting a bit in the other thread
> (https://mail.mozilla.org/pipermail/es-discuss/2012-August/024602.html)
> but the more I think about it, the more it makes sense. JS should have
> an Error primitive type. It would make the "failed" return type for
> most actions more consistent. The word consistent is not without a bit
> of irony, due to the nature of the Error type, which you can read
> below.
>
> Tl;dr the Error type would be instanceof the Error object (like
> strings are to String). It would have an internal property containing
> an error message, which can be empty. Comparison and coercion would be
> very special, in that it should be able to mimic all the fail values
> currently used in JS. This includes -1 when coercing to a number, null
> when coerced to an object, and false when coerced to a boolean. It
> always compares equal to itself (like the different NaNs do) and could
> only be distinguished from one another by checking the result of
> error.toString(). An Error type value would be created through
> Error.primitive('foo'). There would also be Error.isError() that works
> like isNaN().
>
> Inconsistent
>
> JS has various ways of letting the user know that an operation has
> failed. Some examples include str.indexOf(), regex.exec(str), and
> delete(window.eval). There is no single way of handling these errors
> and there's no way to get a more specific reason from these failures
> because the returned values are primitives. So unless a method throws
> explicitly, you're just stuck with a "computer says no".
>
> New type
>
> So let's introduce a new type; the Error type. A value that's
> indistinguishable from exisitng error denoting values, but that still
> holds a special semantic value. It would also be able to hold a
> message interally, one you could only get by calling .toString() on
> it.
>
> Backwards compat
>
> Of course, introducing a new type this late in the game is a problem.
> The JS language does not have the luxery of simply introducing
> language breaking features when moving to a new major version. I don't
> think that needs more explaining, we all know this. However, I think
> this Error type could be introduced while keeping virtually all
> language semantics as they are. This does mean the type will have some
> very ugly semantics. But those should not really bother the user that
> does not want to use them.
>
> We don't want to change the API for these existing mechanics because
> that would be too breaking. So instead we could introduce a type that
> wouldn't. It just so happens to be that for the various types of
> errors JS might know about, it always returns at least the same
> primitive value for any type. Meaning NaN or -1 for number, false for
> boolean, undefined and null for ... well, undefined and null. (The
> only one I'm not sure about is zeroes. I think all the API's that
> might return a number, return -1 for failure or NaN for computational
> issues, but maybe I'm missing one that returns zero...?) So let's make
> this Error type match and coerce to all these types...
>
> In other words, when comparing (weak, strict, or relative), always
> convert the Error type to the other type explicitly according to this
> table:
>
> Undefined -> undefined
> Null -> null
> Boolean -> false
> NaN -> NaN
> non-NaN Number -> -1
> String -> the error message? there's no fail return value that returns a string
> Object -> null
> Error -> error
>
> I'm not sure about String, but since there's currently no API that
> returns a string in case of errors, this could just return the
> internally stored error message. Could allow one to compare error
> messages easily... Individual errors should be indistinguishable from
> one another in comparisons. They'd behave like NaN in that regard (in
> the sense that there are different NaN values but in JS we can't
> distinguish them).
>
> Error
>
> I don't want to bog down the syntax with a literal for this Error type
> and I don't think that's even necessary. The fact that "error" might
> be a pretty common keyword only adds to this. But I think it'd be
> quite elegant to create Error type values through
> Error.primitive(msg). (Ok, I started this with `fail` as the name of
> the type, so `primitive` is not as elegant, but feel free to bikeshed
> that into something better ;)) In fact, I think it would make very
> much sense to make error an instance of Error. We can add special
> cases for calling Error(primitiveError) to behave like String("foo")
> would. Error.prototype.toString would also become a special case for
> Error type. Or rather, it would probably be extended to first check
> for an internal [[ErrorMessage]] property before checking an own
> message property.
>
> (We could make Error('foo') return a primitive instead of a new Error
> object, but I think there's too much legacy usage of calling Error to
> make that change now)
>
> So the built-in Error object would get two new properties;
> .primitive(msg:string) and .isError(val:any). Luckily the Error object
> is not as popular to extend as String or Number are, so I think the
> chances on collisions for these methods are small (though I don't have
> any actual data on this).
>
> Typeof
>
> I'm not sure about the result of the typeof operator. I think typeof
> could/should simply return "boolean", to keep it backwards compat as
> much as possible. Yes, that would kill it for the cases where you're
> checking for "null" (regex related methods) or "number" (but who does
> that...?), but I'm not sure I've ever seen code that uses typeof to
> check for an error. Which makes sense because in most cases the
> returned type is the same as when there was no error. Only for regex
> cases the type is different (in that Null is it's own type), but
> people simply do an if check in that case. So I'd say boolean to
> prevent old code that checks for primitives and would skip this.
>
> Built-in messages
>
> An error type could help to give some better insight to why a certain
> error was thrown. Although in most cases the error can only be one
> (like -1 being "not found" for array.indexOf) there are some cases
> where it might (like "why am I getting NaN here?"). So the
> specification could specify default error messages for certain steps
> in the algorithm. We could choose to specify the actual error, or go
> the route of AS3 and only specify error codes. An implementor could
> then decide on its own what the textual message should be. Especially
> in multi-language environments, I think that would make more sense.
> Also with regards to dynamic content ("#1003: tried to multiply 'foo'
> by 5 in foo.js:53"), error codes would be the way to go. Code would
> only have to take a fixed substring of the error message in order to
> determine which built-in error ocurred.
>
> Pros
>
> - It would make give the language a tool for better semantics in cases
> of an error
> - It's fully backwards compatible, no exception
> - It's very flexible
> - Allows you to add a message to your error (without throwing, while
> keeping backwards compat for primitives)
> - A primitive Error type makes a lot of sense and would be consistent
> with some of the other built-ins
> - Easier debugging for NaN cases
>
> Cons
>
> - It's a weird type
> - Adding a new type to the language
> - Weird constructions possible
> - Inconsistent type
> - Potentially very confusing
> - What to do with typeof
> - We already have throw for explicit errors
>
> error vs fail
>
> I started writing this proposal with "fail" as the name of the type,
> but later figured that "error" makes much more sense. Especially with
> there already being an Error built-in object. And of course there's
> already the precedence of all other primitive types that have a parent
> class.
>
> Conclusion
>
> I think the Error type would be nice to have. I wouldn't mind it if
> the spec was a bit more consistent in the area of errors and I
> certainly would like to see more clarity on certain errors. Why did
> that delete fail? Why am I getting a NaN? Currently there's no easy
> way of getting to the bottom of that. I think it would be a good
> addition to the language. I feel it does not change the language in a
> way that drives it away from being "like JS".
>
> I don't think this has a high chance of making it into the language
> though. This would be a pretty radical thing to appear in the
> language, even if it's just due to it's very dynamic nature. Consider
> other factors that come into play for this decision and I don't think
> the odds are in my favor. But I still think it's worth putting this
> idea on the table.
>
> So I hope you liked it :)
>
> - peter
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss



More information about the es-discuss mailing list