With the question Listing all the contents of a directory by breadth-first order results in low efficiencyI learned that the low efficiency is due to a strange behavior of t
The problem isn't the definition of sequence
, it's the operation of the underlying monad. In particular, it's the strictness of the monad's >>=
operation that determines the strictness of sequence
.
For a sufficiently lazy monad, it's entirely possible to run sequence
on an infinite list and consume the result incrementally. Consider:
Prelude> :m + Control.Monad.Identity
Prelude Control.Monad.Identity> runIdentity (sequence $ map return [1..] :: Identity [Int])
and the list will be printed (consumed) incrementally as desired.
It may be enlightening to try this with Control.Monad.State.Strict
and Control.Monad.State.Lazy
:
-- will print the list
Prelude Control.Monad.State.Lazy> evalState (sequence $ map return [1..] :: State () [Int]) ()
-- loops
Prelude Control.Monad.State.Strict> evalState (sequence $ map return [1..] :: State () [Int]) ()
In the IO
monad, >>=
is by definition strict, since this strictness is exactly the property necessary to enable reasoning about effect sequencing. I think @jberryman's answer is a good demonstration of what is meant by a "strict >>=
". For IO
and other monads with a strict >>=
, each expression in the list must be evaluated before sequence
can return. With an infinite list of expressions, this isn't possible.
You're not quite grokking the mechanics of bind:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
Here's an implementation of sequence that only works on 3-length lists:
sequence3 (ma:mb:mc:[]) = ma >>= (\a-> mb >>= (\b-> mc >>= (\c-> return [a,b,c] )))
You see how we have to "run" each "monadic action" in the list before we can return the outer constructor (i.e. the outermost cons, or (:)
)? Try implementing it differently if you don't believe.
This is one reason monads are useful for IO: there is an implicit sequencing of effects when you bind two actions.
You also have to be careful about using the terms "lazy" and "strict". It's true with sequence
that you must traverse the whole list before the final result can be wrapped, but the following works perfectly well:
Prelude Control.Monad> sequence3 [Just undefined, Just undefined, Nothing]
Nothing
Monadic sequence
cannot in general work lazily on infinite lists. Consider its signature:
sequence :: Monad m => [m a] -> m [a]
It combines all monadic effects in its argument into a single effect. If you apply it to an infinite list, you'd need to combine an infinite number of effect into one. For some monads, it is possible, for some monads, it is not.
As an example, consider sequence
specialized to Maybe
, as you did in your example:
sequence :: [Maybe a] -> Maybe [a]
The result is Just ...
iff all elements in the array are Just ...
. If any of the elements is Nothing
then the result is Nothing
. This means that unless you examine all elements of the input, you cannot tell if the result is Nothing
or Just ...
.
The same applies for sequence
specialized to []
: sequence :: [[a]] -> [[a]]
. If any of the elements of the argument is an empty list, the whole result is an empty list, like in sequence [[1],[2,3],[],[4]]
. So in order to evaluate sequence
on a list of lists, you have to examine all the elements to see what the result will look like.
On the other hand, sequence specialized to the Reader
monad can process its argument lazily, because there is no real "effect" on Reader
's monadic computation. If you define
inf :: Reader Int [Int]
inf = sequence $ map return [1..]
or perhaps
inf = sequence $ map (\x -> reader (* x)) [1..]
it will work lazily, as you can see by calling take 10 (runReader inf 3)
.