
Excerpts from Henning Thielemann's message of Sun Mar 22 22:52:48 +0100 2009:
On Sun, 22 Mar 2009, nicolas.pouillard wrote:
Excerpts from Henning Thielemann's message of Sat Mar 21 22:27:08 +0100 2009:
Maybe you know of my packages lazy-io and explicit-exception which also aim at lazy I/O and asynchronous exception handling.
I was indeed aware of these two packages but I think they hold orthogonal ideas.
About the lazy-io package, as explained in the documentation one has to carefully choose which operations can be lifted. In safe-lazy-io I try to choose a set of well behaving combinators to replace 'getContents' in the IO monad.
Moreover if I take the three problems of standard lazy IO in turn: 1/ Control of resources: One advantage over standard lazy IO is that the file opening can also be done lazily, avoiding an immediate resource exhaustion. However one still relies on evaluation and garbage collection to take care of closing handles, which is not satisfying since handles are scarce resources. 2/ Control of exceptions: If one writes a 'getContents' function such that it no longer hides I/O errors during reading, how do you guarantee that exceptions will happen during the LazyIO.run and not after?
Currently I cannot guarantee anything. However my idea is to stay away from built-in exceptions in IO. In explicit-exception there is an experimental hidden module which provides an IO monad wrapper called SIO which cannot throw any IO exception. http://code.haskell.org/explicit-exception/src/System/IO/Straight.hs Actually, I think it's the wrong way round to build an exception-free monad on top of one with exceptions. Instead IO should be built on top of SIO, but that's not possible for historical reasons. The only safe operation to get into SIO is ioToExceptionalSIO :: IO a -> ExceptionalT IOException SIO a That is, it makes exceptions explicit and SIO operations can never throw IO exceptions. You should convert synchronous explicit exceptions of atomic operations like getChar into asynchronous explicit exceptions, combine them lazily to big operations like getContents. Then you get getContents :: SIO (Asynchronous.Exception IOException String) If you run lazy SIO operations you can't become surprised by exceptions.
It sounds like a nice idea, it would be great to have a straight-io package to play a bit more with explicit exceptions in things like 'IO'. For safe-lazy-io I wanted to keep the exception management as light as possible. In particular when writing programs where most of the 'IO' errors are considered fatals---EOF is not fatal of course but using getContents one do not see it.
3/ Determinism: when freely combining multiple inputs one risks the problem mentioned by Oleg [1], when using your package it will depend on the 'getContents' function we use: a) if we 'liftIO' the standard 'getContents' function, we can have the issue. b) if we write a new 'getContents' as below [2], then (if I got right your lazy IO monad) all reads are chained. And then one has to process inputs in the same order.
I wouldn't build hClose into getContents, because you never know, whether the file is read until it's end. If you call a LazyIO.getContents twice, the contents are read sequential. In order to read file contents simultaneously you must call (LazyIO.run LazyIO.getContents) twice in the IO monad.
Right but one of the purposes of safe-lazy-io is to provides a good management of file handles in particular closing them. Actually the implementation of lazy inputs focus particularly on that---through the 'Finalized' values. http://hackage.haskell.org/packages/archive/safe-lazy-io/0.1/doc/html/src/Sy... -- Nicolas Pouillard