
Chad Scherrer:
Ok, that looks good, but what if I need some random values elsewhere in the program? This doesn't return a new generator (and it can't because you never get to the end of the list).
To fix that, just replace the call to getStdGen with a call to newStdGen, as has already been suggested by Sebastian. Internally, newStdGen uses "split" to produce an independent generator, and also updates the global generator. Thus, repeated calls to newStdGen all return independent generators. So, bringing together the suggestions so far, we get this:
import Random import Array
-- from Lauri Alanko randomElts rg xs = map (arr !) (randomRs bds rg) where bds = (1, length xs) arr = listArray bds xs
-- see discussion below inspect_stream = foldr (\x -> (seq x).(x:)) []
main = do let foo g = drop 10000000 $ inspect_stream $ randomElts g [10,2,42::Int] g1 <- newStdGen g2 <- newStdGen print $ take 10 $ foo g1 print $ take 10 $ foo g2
No space leaks, independent lazy infinite random sequences. I was somewhat dismayed that I needed to write inspect_stream to make the demonstration using drop work without a stack overflow. This is because the current implementation of randomRs is quite lazy, making it very easy to build up huge thunks which blow the stack if you don't evaluate them from the inside out. For most applications, where you inspect the random numbers in roughly the same order as you extract them from the sequence, this wouldn't be a problem. Are there real applications (as opposed to toy demonstrations) where this wouldn't be the case? I don't know, but perhaps the question of whether randomRs should be more strict warrants some discussion.