On Tue, Feb 4, 2014 at 9:49 AM, Domenic Denicola <[email protected]> wrote: > From: es-discuss <[email protected]> on behalf of Allen > Wirfs-Brock <[email protected]> > >> I've designed exception handling systems before and designed and worked with >> complex exception hierarchies. My main caveat is that they are somewhat of >> an attractive nuisance. It's easy to get into designing beautiful >> classification hierarchies of dozens of different kinds of exceptions, each >> with their own specific failure information that is captured. In practice, >> I've found very little actual utility for them. > > My (more limited) experience is the same.
I agree. Creating hierarchies of error "types" seems fragile to me. A better approach seems like having a short list of flat error types. In cases where it might be useful to have a more specific error type, instead include additional information on the error. So for example, rather than having a NetworkResourceNotFound error, use NetworkError but add a property which indicate what type of network failure. Not sure if that was what you were proposing with the "proximate cause"? More on that below. I think the general direction that we've been heading lately has been pretty good. I.e. error types like NetworkError, QuotaExceededError are descriptive and have clear use cases for code to catch and handle. On the flip side, the DOM also has some overlapping errors that are too specific. IndexSizeError should simply have been RangeError, no need to use a separate exception because it was an index that was out-of-range, rather than some other type of argument. Then there's a bunch of errors which are unclear why you'd ever want to catch them at runtime. For example HierarchyRequestError, NotFoundError, NamespaceError and URLMismatchError is unclear to me why you'd ever want to catch. It seems useful to provide detailed information so that a developer is helped with understanding what went wrong, but that can be done through the .message property. TypeError is probably fine for many of these. Though I sort of hate its name and would be happy to see something more descriptive become available. > However, I think the utility that the DOM specs try to capture with their > errors is different than that captured by the exception hierarchy that one > might think of when looking at them. Let me set the stage: > > There seem to be a few possible properties of an exception that could be > varied depending on the situation: > > - The type. Typically this is very broad in ES, and in the DOM even more so > (everything is `DOMException`). In ES this is the same as the `.name` > property, but not in the DOM. > - The category. ES has no such concept, but DOM specs use the `.name` > property for this, with a variety of generic categories like > "InvalidStateException," "NetworkError," etc. [1] I'm not sure that there's a difference between these two. Or at least I don't think there's a difference between the stuff that the DOM puts in .name, and that ES puts in the classname. They express the same thing just in different ways. But that might not be relevant to your proposed solution. > - The human-readable message. This is never specified, from what I can see. I don't think we should specify the exact string that goes into these messages. It's good if implementations are free to add additional details if they see developers struggling with something. But we could definitely do with more recommendations for implementations about what information to include here. > - The proximate cause. By this I mean the exact situation or line of the > relevant spec that caused the error to be thrown. Every such cause should be > distinguishable, even if they belong to the same category. Usually there will > be a one-to-one correspondence between human-readable messages (or message > format strings, with appropriate placeholders) and machine-readable proximate > causes. I've seen this rarely, but some parts of Node.js use error codes for > this. > > I have not seen much attention paid to the "proximate cause" idea in the > wild. But I think it is perhaps the most important. Allen's point about > hierarchies being largely useless resonates with me on the type and category > level. That is, even the DOM's more fine-grained exception categorization is > not that useful if I am trying to recover from a specific scenario. Whereas, > the ability to specify specific situations to recover from would be much more > fruitful. > > For a great example of these levels, see [recent discussions on the streams > repo][2]. We have two situations: writing to a closing stream, and writing to > a closed stream. On an ES level, these would end up as both `TypeError`s, as > [discussed][3], with no further distinguishing information besides > implementation-defined `message`. Working on a DOM level would be no > different, I think: both would likely become `DOMException`s with `name` of > `"InvalidStateError"`. What we really want is a machine-readable (and thus > rigorously specifiable) "proximate cause" field that consumers could use to > distinguish which situation they got themselves into, especially e.g. in > logs. As-is, we just [folded them both into `TypeError`][4] with no detail on > the cause, hoping implementations would be helpful about this. > > All this said, I think the DOM's categorization, and my proximate cause idea, > are both trying to allow you to distinguish failure modes. You could > envision, especially [with better syntax support][5], letting most errors > bubble, but catching and handling `NetworkError`s with a retry. It would be > even better, I claim, to be able to handle "network error from no network" > separately from "network error due to server misbehavior", thus the proximate > cause idea. This divide, between categorization and proximate cause, is part > of why I think the DOM's exception categories evoke semi-useless exception > hierarchies, even if they are aiming at a useful goal. I don't fully understand everything you are saying above, but what I do understand I agree with :) I think a proposed solution would help clarify. For your Stream example above I would propose using an InvalidStateError but give it a couple of properties so that code could understand what went wrong. Maybe a property which contains "BaseWritableStream.write" to indicate that that was the function that failed, and a second property which contains "closing" or "closed" to indicate which wrong state was hit. This is definitely a very unpolished proposal, but might illustrate something that could work. > As you can see, I Have Opinions on this. I also know a number of community > members who do as well. I'd love to discuss this further and perhaps pick up > on that strawman idea you are suggesting, Allen. That'd rock! / Jonas _______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

