I'm learning Haskell and currently trying to wrap my head around monads. While playing with some random number generation I got tripped on lazy evaluation once again. In an effort to simplify something close to the:
roll :: State StdGen Int
roll = do
gen <- get
let (n, newGen) = randomR (0,1) gen
put newGen
return n
main = do
gen <- getStdGen
let x = sum $ evalState (replicateM iterations roll) gen
print x
into something like this:
roll' :: IO Int
roll' = getStdRandom $ randomR (0,1)
main = do
x' <- fmap sum $ replicateM iterations roll'
print x'
on a larger number of iterations, let's say 1000 * 1000 * 10, second example results in a stack overflow.
Why is the first version happily runs in constant space and the second one blows up?
Speaking more broadly, can you recommend some reading to improve one's mental model of Haskell's lazy evaluation? (Introductory to intermediate level, preferably.) Because when it comes to evaluation in Haskell, my intuition completely fails me.