[Haskell-cafe] Why doesn't this work?

Duncan Coutts duncan.coutts at worc.ox.ac.uk
Mon Apr 25 05:39:53 EDT 2005


On Sun, 2005-04-24 at 23:16 -0700, Michael Vanier wrote:
> I've been trying to generate an infinite list of random coin flips in GHC
> 6.4, and I've come across some strange behavior:
> 
> ----------------------------------------------------------------------
> import System.Random
> 
> data Coin = H | T deriving (Eq, Show)
> 
> -- Generate a random coin flip.
> coinFlip :: IO Coin
> coinFlip = do b <- getStdRandom random
>               return (bool2coin b)
>            where
>               bool2coin True  = H
>               bool2coin False = T
> 
> -- Generate an infinite list of coin flips.
> coinFlips :: IO [Coin]
> coinFlips = sequence cfs
>             where cfs = (coinFlip : cfs)
> 
> -- Print n of them.
> test :: Int -> IO ()
> test n = do f <- coinFlips
>             print (take n f)
> ----------------------------------------------------------------------
> 
> Now when I do "test 1" (for instance), it hangs forever.  It seems as if
> there is some kind of strictness constraint going on that I don't
> understand.  My understanding is that cfs is an infinite list of (IO Coin),
> sequence lifts this to be IO [Coin] where [Coin] is an infinite list, and
> then test should extract the infinite list of coin flips into f, take some
> number of them, and print them.  But instead, the system appears to be
> trying to compute all the coin flips before taking any of them.  Why is
> this, and how do I fix it?

My first guess is that this is because sequence is strict in it's list.
This is the normal behaviour that you would expect for this function
since otherwise the side effects from all the IO actions are not going
to happen before it returns (which is the ordinary behaviour for IO
actions; one of the main purposes of the IO monad is for sequencing side
effects).

You can lazily defer IO actions using unsafeInterleaveIO.

However in this case that's probably not the most elegant approach. It
might be better to make the coinFlip function pure (ie not in the IO
monad) and instead to pass it a random number generator which it returns
as an extra component of the result (having extracted a random value
using 'random'). Then you can use getStdGen once and pass the result to
a function which generates an infinite lazy list of random numbers by
threading the generator between calls to coinFlip. (Or if you want to
cheat you can use randoms which will do all this for you)

See:
http://haskell.org/ghc/docs/latest/html/libraries/base/System.Random.html

Duncan



More information about the Haskell-Cafe mailing list