[Haskell-cafe] [ANN] Safe Lazy IO in Haskell

Henning Thielemann lemming at henning-thielemann.de
Sun Mar 22 17:52:48 EDT 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.
  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.

> 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.

More information about the Haskell-Cafe mailing list