
On Sun, Dec 6, 2009 at 10:40 PM, Alexander Dunlap
On Sat, Dec 5, 2009 at 3:00 PM, Michael Snoyman
wrote: On Sun, Dec 6, 2009 at 12:55 AM, Henning Thielemann
wrote: On Sun, 6 Dec 2009, Michael Snoyman wrote:
I think there are plenty of examples like web servers. A text editor with plugins? I don't want to lose three hours worth of work just because some plugin wasn't written correctly. For many classes of programs, the distinction between error and exception is not only blurred, it's fully irrelevant. Harping on people every time they use error in the "wrong" sense seems unhelpful.
Hope my commenting on this subject doesn't become my own form of *pedantry*.
In an earlier thread I have explained that one can consider a software architecture as divided into levels. What is an error in one level (text editor plugin, web server thread, operating system process) is an exception in the next higher level (text editor, web server, shell respectively). This doesn't reduce the importance to distinguish between errors and exceptions within one level. All approaches so far that I have seen in Haskell just mix exceptions and errors in an arbitrary way.
I think we can all appreciate why it would be a bad thing is we treat exceptions as errors. For example, I don't want my program to crash on a file not found.
On the other hand, what's so bad about treating errors as exceptions? If instead of the program crashing on an array-out-of-bound or pattern-match it throws an exception which can be caught, so what?
Michael
I think the key is in the difference between the user/client and programmer/developer. As Henning has been saying, these roles change as you go through the different levels of the program, but I see the difference between an error and an exception as this: when a problem is relevant to the user/client, it's an exception; when it is irrelevant to the user/client, it's an error. Suppose you were using some sort of exception framework and you got an error from a piece of library code (not the head function) saying that "head" had failed somewhere. This is absolutely meaningless to a client. It just means there's a problem in the library code; it doesn't mean anything is amiss in the client's space. The client basically has to throw the function out, whether by gracefully aborting the program, disabling the plugin, etc. Contrast this with an exception, such as "index not in map." This is entirely relevant to the client. All of the code knows exactly what is going on; it's just that the index isn't in the map. The client can recover from this by, say, substituting a default value, adding the index to the map, etc. Now, suppose the client knew a priori that the index was *supposed* to be in the map. Now this becomes an *error* to the *client* of the client, since there is a bug in the first client's code.
I guess my point is that if I have a function, say, sort :: Ord a => [a] -> [a], and sort calls head somewhere in it, there's no point having "sort" throw an exception for "Prelude.head: []" since that means nothing to the client. It would make more sense to have an InternalError type that just says "OK, sorry client, I screwed up, just clean things up as best you can". If really were something that the client could have responded to, sort should have caught the head error inside the function definition and rethrown a different exception; say, SortEmptyListError if your sort function didn't work on empty lists (contrived example, sorry).
Alex
(Sorry for replying to myself.) This is, of course, assuming that a non-empty list was not an invariant of the sort function, in which case it would be a programming error on the part of the client that called sort if that happened. Alex