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 ditto. 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, /Josef