Non-monolithic version of 'sequence' for IO monad?

Josef Svenningsson
Tue, 6 Nov 2001 14:20:11 +0100 (MET)

On Tue, 6 Nov 2001, Koen Claessen wrote:

> Malcolm Wallace wrote:
>  | > sequence :: (Monad m) => [m a] -> m [a]
>  | > sequence = foldr (liftM2 (:)) (return [])
>  | Are you *really* sure that the latter consumes its
>  | entire input before producing any result?  It looks
>  | pretty lazy to me, and I seem to be able to give it an
>  | infinite list of computations and yet start receiving
>  | results immediately....
> This is not true, as the following example demonstrates:
>   main :: IO ()
>   main = sequence [ return i | i <- [1..] ] >>= print
> The IO monad is strict. This means that one cannot look at
> the result `a' of a computation of type `IO a' before all
> the IO in that computation is done.
The statement "The IO monad is strict" is a bit too strong. Indeed, the IO
monad is strict in all implementations (and rightly so). But I cannot find
anything in the report saying that the IO monad is strict.

> (Note that in other monads that are not strict, the above
> computation works without problems.)
Let me elaborate on the strictness of a monad. The strictness of a monad
depends on how one implements the `bind' function. If the monad has a
strict bind then in examples like Koen's the print function will never be
called. If bind is lazy then the list will be produced incrementally.

For many monads it makes sense to have both a strict and a lazy instance.
One example is the ST monad which comes in both flavours. So why is it
useful to have both? The above situation shows that one may expect a monad
to have a lazy behavour. In these cases the lazy instance is good.
But lazy monads can have really bad memory behaviour and that is why the
ST monad is strict by default. For instance, monadic tail calls can be
implemented efficiently with with strict instances but not with lazy

Why am I writing all this? I think the previous discussion shows that the
Haskell community is not very aware of the subtleties in strict versus
lazy monads. I hope I've shed some light, or at least made people start to
think about it.

All the best,