I'm having difficulty trying to figure out a way to reason about why the following two, seemingly equivalent definitions of an infinite random number sequence (inf and inf') are evaluated completely differently:
import Control.Monad.Random (Rand, evalRandIO, getRandom)
import System.Random (Random, RandomGen, randomIO)
inf :: (RandomGen g, Random a) => Rand g [a]
inf = sequence (repeat getRandom)
inf' :: (Random a) => IO [a]
inf' = sequence (repeat randomIO)
-- OK
main = do
i <- evalRandIO inf
putStrLn $ show $ take 5 (i :: [Int])
-- HANGS
main' = do
i <- inf'
putStrLn $ show $ take 5 (i :: [Int])
when called, main' terminates and prints 5 random integers, whereas main loops infinitely — what causes sequence . repeat to be evaluated differently on getRandom than it does on randomIO?
Sequencing lists is strict in the IO monad but possibly lazy in the State monad.
Randis just a wrappedStateT, so it can be lazy:evalRandIOqueries the IO random number generator just once at the beginning, then runs theState-ful computation on the acquiredStdGen:In contrast,
sequence $ repeat randomIOcontains an infinite number of side effecting IO actions, because eachrandomIOmodifies the global random number generator. We can't inspect the return value until all the effects are performed. It's similar to doingsequence $ repeat getLine, which just reads lines repeatedly, and never returns.