The Error type

2012-08-16

There was a thread on es-discuss that was trying to start a discussion about the various ways JS returns a failure value (that's not throwing). I jokingly replied that we should introduce a new type for this. But the more I thought about it, the more it made sense to me. So here's a proposal for such a type. It's not very formal and it probably did not cover all edge cases. On top of that I'm afraid it's pretty late for the current draft to introduce such a thing from scratch. Many people will have many ideas to say about this, I'm sure.

Anyways. Below is the idea behind my original post.

Tl;dr the Error type would be instanceof the Error object (like strings are to String) the primitive to 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 never 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().

(Thanks Andrea for correcting me on the instanceof issue, I spent too much time in as3 land the past few months...)

Again, this is a new PROPOSAL. This is not part of the JS language and there are currently no plans, not even remotely, to add it.

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 primitive value that's indistinguishable from existing error denoting values, but that still holds a special semantic value. It would also be able to hold a message internally, 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 luxury 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.

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 ;)) 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. And also with regards to dynamic content ("#1003: tried to multiply 'foo' by 5 in foo.js:53"). Code would only have to take a fixed substring of the error message in order to determine which built-in error occurred.

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 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. Let alone other factors that come into play for this decision and I don't think the odds are in my favour. But I still think it's worth putting this idea on the table. Or even in a blog post.

So I hope you liked it :)