
Is this really a solution? Currently, getContents reports no errors but does perfect error recovery: the result of the computation prior to the error is preserved and reported to the caller. Imprecise exceptions give us error reporting -- but no error recovery. All previously computed results are lost. Here's a typical scenario: do l <- getContents return (map process l) If an error occurs reading lazy input, we'd like to log the error and assume the input is terminated with EOF.
i was wondering: could you perhaps use the old Hood Observe trick to help you out? Hood wraps lazy sources in unsafePerformIO handlers that log things as they are demanded, then passes them on. you might be able to use the same to pass things on as they are demanded, while logging exceptions and replacing exceptional with repaired or default values?
What we'd like are _resumable_ exceptions. The exception handler receives not only the exception indicator but also the continuation where the exception has occurred. Invoking this continuation means error recovery. Resumable exceptions are used extensively in CL; they are also available in OCaml. So, hypothetically we could write do l <- getContents resume_catch (return (map process l)) (\e k -> syslog e >> k [])
Besides the obvious typing problem, this won't work for the reason that exceptions raised in the pure code are _imprecise_ -- that is, no precise continuation is available, even in principle.
yes, i've often wondered why exceptions are not optionally resumable, with a handler that may decide to return or abort, depending on how seriously the protected code is likely to be affected by the exception in hand. that way, exception handling and normal processing would be better separated, and simple fault tolerance easier to achieve, whereas now the exception handler would have to know how to restart the interrupted computation from scratch. in terms of imprecise semantics, that might not be a problem, either: yes, you can't guarantee that the same exception will be raised if you repeat the experiment, but that is why the handler is in IO already. no matter what exception and continuation it receives, if the exception is non-fatal, it can log the problem and return, with a repaired value, to the pure code that raised the exception. in terms of stack-based implementation, it might be the simple difference of clearing the stack in the handler, rather than in the raiser, giving the handler the option to resume or abort the raiser. or is that too naive?-) claus