
the usual caveats about unsafePerformIO apply, so perhaps you wouldn't want to use this in a database library..
Indeed. This is quite problematic, from the practical point of view of making resources difficult to control (cf. another thread of file handle leakage), to the theoretical point that side effects and lazy evaluation strategy is a bad mix, severely limiting the equational theory and making the code hard to reason about. I do care about all of these issues; otherwise I would have programmed in C. That reminds of Simon Peyton-Jones POPL2003 presentation, the retrospective on Haskell. He said that the fact that lazy evaluation and side effects are poor match has kept the designers from adding all kinds of problematic hacks to the language. The laziness has kept Haskell pure -- until the monad (notation) has come along and showed how to do side-effects in the principled way. If keeping the purity and keeping unsolved problems open until a principled solution comes along have worked so well in the past, why to change now? As to the original question
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)
a better (albeit still quite unsatisfactory) answer might be to change the interface of getContents so it would take the handler as an argument: newGetContents :: (Exception -> IO String) -> IO String The old getContents is equivalent to "newGetContents (const (return []))". If the handler needs to notify the rest of the program of an error, it may save the information from the exception in a IORef defined in outer scopes. If this looks like the inversion of control, that's because it is... Often the problem can be solved via a left-fold enumerator, like the one in Takusen. In the context of reading file, such an enumerator is described in http://okmij.org/ftp/Haskell/misc.html#fold-stream One of the examples in that article was specifically reading only a few characters from a file. With enumerator, we guarantee that file handles do not leak, that files are closed at the precise and predictable moments, and we never read the whole file in memory unless the programmer specifically wishes to.