1) System.Random.randoms
Simplest way to generate an infinite list of random numbers.
import System.Random as R
main = do
gen <- R.getStdGen
print $ take 10 $ R.randoms gen
Internally, randoms
construct an infinite list:
randoms g = (\(x,g') -> x : randoms g') (random g)
2) State monad
The previous method is simple, but doesn't return a generator, so effectively, the generated numbers are the only one available. To get some random variable and a new generator, the state monad can be used.
import Control.Monad.State
genRng :: RandomGen g => State g Int
genRng = do
gen <- get
let (val, gen') = R.random gen
put gen'
return val
main = do
gen <- R.getStdGen
-- get 10 random numbers and the final generator
let (vals, finalGen) = runState (replicateM 10 genRng) gen
print vals
This approach also lends itself to more complex operation, for example, returning a tuple:
genRngTuple = do
gen <- get
let (val1, gen') = R.random gen
let (val2, gen'') = R.random gen'
put gen''
return (val1, val2)
3) Simplifing with state
The genRng
function is a bit verbose and can be simplified with state
import Control.Monad.State
genRng' :: RandomGen g => State g Int
genRng' = state R.Random
main = do
gen <- R.getStdGen
print $ evalState (replicateM 10 genRng') gen
This approach is much nicer when returning tuple for example:
genRngTuple' = do
a <- state R.random
b <- state R.random
return (a, b)
This way, no need to do any bookkeeping with the internal state.
Here are three ways to generate multiple random values. These method works for any kind of stateful computation. The monad approach gives more control, allowing any function like s -> (a, s)
to be embedded inside, abstracting away any work to thread the state across calls.
The State monad can also be generalized using monad transformer. Some good read about the transformer approach:
- Send more money
- and the follow up: Sending even more money.