lazily handling exceptions in lazy sources (Re: [Haskell-cafe] Re: Takusen and strictness, and perils of getContents)
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? attached is an implementation sketch, with an example problem (a lazily produced String that never gets its 10th element, and includes an error for every uppercase Char in the input. with the handlers commented out, we get things like this: $ (echo Hi ; sleep 1; echo There, World) | runHaskell ResumeCatch.hs -- *** Exception: oops while replacing str with a handled str (in the definition of safestr) gives: $ (echo Hi ; sleep 1; echo There, World) | runHaskell ResumeCatch.hs -- ?I ?HERE -- ?I ?HERE{- ResumeCatch.hs:30:8-57: Non-exhaustive patterns in function process -} all is well that ends well -- the usual caveats about unsafePerformIO apply, so perhaps you wouldn't want to use this in a database library.. claus ResumeCatch.hs Description: Binary data ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: Takusen and strictness, and perils of getContents
[EMAIL PROTECTED] wrote: Simon Marlow wrote: Anyway, I just wanted to point out that nowadays we have the option of using imprecise exceptions to report errors in lazy I/O. 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. If getContents raises an imprecise exception, what do we do? return (map process (catch l (\e - syslog e return []))) That of course won't work: l is a value rather than an effectful computation; besides; catch can't occur in the pure code to start with. 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 think I agree with that. Resumable exceptions don't make any sense for pure code (I can certainly imagine implementing them though, and they make sense in the IO monad). But all is not lost: if an exception is raised during getContents for example, you still have the partial results: the list ends in an exception, and I can write a function that returns the non-exceptional portion (in IO, of course). Cheers, Simon ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Takusen and strictness, and perils of getContents
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 ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe