
On Wed, 9 Jul 2008, David Roundy wrote:
On Wed, Jul 09, 2008 at 04:17:25PM +0200, Henning Thielemann wrote: ...
There is also another unintended application: Escaping from loops or deep recursions, maybe even returning a result by an exception. Actually, in order to define the interaction of abort mechanisms like RETURN in a PROCEDURE or EXIT in LOOP, the Modula-3 designers defined RETURN and EXIT in terms of exceptions (although they suggested to implement them more efficiently). These abuses of exceptions made Niklaus Wirth, the inventor of Pascal, fear, that exceptions bring back a GOTO into structured programming languages.
Incidentally, the Haskell standard libraries (although not the report) also implement exitWith in terms of an exception. Personally, I think this is a great idea, as I'd rather not deal with two separate mechanisms for cleaning up in unexpected cases (i.e. bugs or exceptions or being passed a function that calls exitWith).
As I said, in case of a bug, it is not possible to reliably clean up. That an error is encountered proves that your assumptions about your program were wrong, and so the assumptions about allocated resources are probably wrong, too. To pick up Chris Smith's perspectives, cleaning up would be the task for the next higher level, for which your error is only an exception - it has hopefully kept track of the resources you allocated.
Having one exception-handling mechanism allows for modular programming, e.g. I can write a function of type
doSomethingThatMightRequireFreeingResources :: ... -> IO a -> IO a
rather than requiring some sort of weird trickery to figure out all the possible ways that my argument might possible fail to return so that I can free whatever resources I need to free.
Which resource to free does normally not depend on the kind of the exception but on the progress of resource allocation, does it? So resource deallocation would work equally with (IO a) and (ErrorT error IO a). It seems that nobody except me is interested in handling specifically (up to a certain level) the exceptional cases that an IO action can lead to. If I see the type signature getLine :: IO String I have no idea, what kind of exceptions can occur. I even have to be prepared to get OpenGL exceptions. Even error codes in C are more informative in this respect. I would have to handle some exceptions which look reasonable to me, and use a catch-all or rethrow-all mechanism for the rest. Possibly the proposers of the extensible exception method like to rely entirely on catch-all or rethrow-all just like errors. Wouldn't getLine :: ErrorT IOError IO String be much clearer?