We use the example to show off explicit streams and simple generators. They too separate consumers and producers. They are not lazy and hence robust. Unlike Haskell lazy lists, they are reliably composable and work in the presence of arbitrary effects. Coding the example lead to a surprise: simple generators form a monad in yet another way.

Dale Jordan hoped to use a Haskell list as a synchronization mechanism: his producer will repeatedly generate chunks (finite lists) of (random) numbers and the ultimate consumer will pick however many it needs. The example relies on State to keep the current state of a random number generator. The State monad is implemented in pure Haskell; inlining the monadic bind should give us pure Haskell code, theoretically. There should not be any impediment to lazy evaluation. And yet we see that even benign effects interfere badly with lazy evaluation.

Dale Jordan message used the following sample chunk producer, generating a length-|n| list of random numbers uniformly distributed within [m,m+9]:

type Rand r = State r something :: RandomGen g => Int -> Int -> Rand g [Int] something n m = sequence . replicate n . state $ randomR (m,m+9)

iterateR

iterateR :: RandomGen g => Rand g [Int] -> Rand g [Int] iterateR m = do chunk <- m (chunk ++) <$> iterateR m

run1 = evalState (take 10 <$> (iterateR (something 2 0))) $ mkStdGen 42 -- [1,1,7,4,6,1,8,1,8,5]

Alas, if we start composing iterateR computations we run into a problem. Neither of the two following expressions terminate. They loop and try to build an infinite list, which quickly crashed the program.

run2 = evalState (take 10 <$> (iterateR (something 2 0) >> iterateR (something 3 10))) $ mkStdGen 42 run3 = evalState (take 10 <$> (iterateR (something 2 0) >>= iterateR . (something 3 . head))) $ mkStdGen 42

The question is easy to answer. Recall, iterateR is a State computation: it receives the current state of the random number generator, and produces a list and the final state. In the expressions run2 and run3 , the second iterateR , before it can run, has to receive the state of the random number generator from the first iterateR . However, iterateR is meant to run forever; it never produces the final state. When we compose the iterateR computation like iterateR ... >>= k , we should bear in mind two entangled data dependencies between iterateR ... and k . One is on the list result of iterateR , which can be computed lazily and consumed lazily. If k also generates random numbers, there is the second dependency, on the state of the generator. Before the generator in k is to yield anything, in needs the fully computed state from the previous generator. Hence the data produced by k depend both on the list and on the state of iterateR ... . It is the second dependency that causes the divergence, that breaks the intended lazy list synchronization. The second dependency is hidden inside the State monad and the implementation of the random number generator, which makes it difficult to see. The overall problem with lazy evaluation is that the data dependencies, which drive the computation, may be numerous, may be hidden in abstraction layers, and are not visible in types.

The first solution is to use a proper stream, which makes it explicit that producing each element requires an effect.

data List m a = Nil | Cons a (ListM m a) type ListM m a = m (List m a)

ListM

ListM

headL

replicateL

appendL

iterateR

replicate

take

somethingL :: RandomGen g => Int -> Int -> ListM (Rand g) Int somethingL n m = sequenceL . replicateL n . state $ randomR (m,m+9) iterateRL :: RandomGen g => ListM (Rand g) Int -> ListM (Rand g) Int iterateRL m = appendL m (iterateRL m)

run

run3L = evalState (takeLL 10 (iterateRL (somethingL 2 0) >>= iterateRL . (somethingL 3 . headL))) $ mkStdGen 42 -- [2,8,5,7,2,9,2,9,6,6]

One may use a fancier stream data type:

data RList m a = RList [a] -- known finite prefix [m [a]] -- a stream of producing actions

Dale Jordan aptly characterized this solution as ``reifying the implicit continuation in my iterateR's recursive definition into a data structure''

Simple generators, described elsewhere on this page, offer another solution. Recall the relevant definitions (which are almost the entire implementation of the simple generators):

type GenT e m = ReaderT (e -> m ()) m type Producer m e = GenT e m () type Consumer m e = e -> m () yield :: Monad m => e -> Producer m e yield e = ask >>= \f -> lift $ f e

Clearly GenT e m is a monad and GenT e is a monad transformer. It may be surprising that Producer m e is also a monad, whose return operation is yield and whose (>>=) we will write as bindG . Re-writing Dale Jordan's code with generators hardly changes the form of the code; iterateR however becomes particularly simple:

somethingG :: (RandomGen g, MonadState g m) => Int -> Int -> Producer m Int somethingG n m = sequence_ . replicate n . (>>= yield) . lift . state $ randomR (m,m+9) iterateRG :: Monad m => Producer m Int -> Producer m Int iterateRG m = m >> iterateRG m

run3G = evalState (takeG 10 (iterateRG (somethingG 2 0) `bindG` (iterateRG . (somethingG 3)))) $ mkStdGen 42 -- [2,8,5,7,2,9,2,9,6,6]

head

run3G

run3

run3L

In conclusion: lazy evaluation is indeed driven by the demand for data, which is its strength -- and downfall: data dependencies can be numerous, entangled, hidden behind abstractions such as monad and interfaces, and not at all expressed in types. Surprises abound. With one hand lazy evaluation promotes modularity, with the other hand, breaks it. Streams and generators separate producers and consumers and promote modularity. With them, however, the evaluation order is explicit and predictable. We have seen that the stream or generator-based code may be just as elegant as the code with lazy lists -- sometimes even more so. As a bonus, it works.