The following program terminates correctly:
import System.Random
randomList = mapM (\\_->getStdRandom (randomR (0, 50000::Int))) [0..5000]
main = do
r
Random numbers in general are not strict, but monadic binding is--the problem here is that mapM
has to sequence the entire list. Consider its type signature, (a -> m b) -> [a] -> m [b]
; as this implies, what it does is first map
the list of type [a]
into a list of type [m b]
, then sequence
that list to get a result of type m [b]
. So, when you bind the result of applying mapM
, e.g. by putting it on the right-hand side of <-
, what this means is "map this function over the list, then execute each monadic action, and combine the results back into a single list". If the list is infinite, this of course won't terminate.
If you simply want a stream of random numbers, you need to generate the list without using a monad for each number. I'm not entirely sure why you've used the design you have, but the basic idea is this: Given a seed value, use a pseudo-random number generator to produce a pair of 1) a random number 2) a new seed, then repeat with the new seed. Any given seed will of course provide the same sequence each time. So, you can use the function getStdGen
, which will provide a fresh seed in the IO
monad; you can then use that seed to create an infinite sequence in completely pure code.
In fact, System.Random
provides functions for precisely that purpose, randoms
or randomRs
instead of random
and randomR
.
If for some reason you want to do it yourself, what you want is essentially an unfold. The function unfoldr
from Data.List
has the type signature (b -> Maybe (a, b)) -> b -> [a]
, which is fairly self-explanatory: Given a value of type b
, it applies the function to get either something of type a
and a new generator value of type b
, or Nothing
to indicate the end of the sequence.
You want an infinite list, so will never need to return Nothing
. Thus, partially applying randomR
to the desired range and composing it with Just
gives this:
Just . randomR (0, 50000::Int) :: (RandomGen a) => a -> Maybe (Int, a)
Feeding that into unfoldr
gives this:
unfoldr (Just . randomR (0, 50000::Int)) :: (RandomGen a) => a -> [Int]
...which does exactly as it claims: Given an instance of RandomGen
, it will produce an infinite (and lazy) list of random numbers generated from that seed.
I would do something more like this, letting randomRs do the work with an initial RandomGen:
#! /usr/bin/env runhaskell
import Control.Monad
import System.Random
randomList :: RandomGen g => g -> [Int]
randomList = randomRs (0, 50000)
main :: IO ()
main = do
randomInts <- liftM randomList newStdGen
print $ take 5 randomInts
As for the laziness, what's happening here is that mapM
is (sequence . map)
Its type is: mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]
It's mapping the function, giving a [m b]
and then needs to execute all those actions to make an m [b]
. It's the sequence that'll never get through the infinite list.
This is explained better in the answers to a prior question: Is Haskell's mapM not lazy?