Haskell Random Generation

haskell, random

Solution

Let's start at the beginning:

type Rand a = State StdGen a

This line tells you that `Rand a` is a type synonym for a `State` type, whose state is given by `StdGen` and whose eventual value is of type `a`. This will be used to store the state of the random number generator between each request for a random number.

The code for `getRandom` can be converted into do notation:

getRandom :: (Random a) => Rand a
getRandom = do
  r <- get                   -- get the current state of the generator
  let (a,g) = random r in do -- call the function random :: StdGen -> (a, StdGen)
    put g                    -- store the new state of the generator
    return a                 -- return the random number that was generated

The `runRand` function takes an initial seed `n` and a value `r` of type `Rand a` (which, remember, is just a synonym for `State StdGen a`). It creates a new generator with `mkStdGen n` and feeds it to `evalState r`. The function `evalState` just evaluates the return value of a `State s a` type, ignoring the state.

Again, we can convert `runRandIO` into `do` notation:

runRandIO :: Rand a -> IO a
runRandIO r = do
  rnd <- randomIO        -- generate a new random number using randomIO
  return (runRand rnd r) -- use that number as the initial seed for runRand

Finally, `getRandoms` takes a number `n` representing the number of random values that you want to generate. It builds a list `[1..n]` and applies `getRandom` to the list. Note that the actual values in `[1..n]` aren't used (you can tell because the lambda function starts with `\_ -> ...`). The list is just there to have something with the correct number of elements. Since `getRandom` returns a monadic value, we use `mapM` to map over the list, which causes the state (i.e. `StdGen`) to be threaded correctly through each of the calls to `getRandom`.

Problem

Could someone describe how the following type constructor and functions work? ``` type Rand a = State StdGen a getRandom :: (Random a) => Rand a getRandom = get >>= (\r -> let (a,g) = random r in (put g) >> (return a)) runRand :: Int -> Rand a -> a runRand n r = evalState r $ mkStdGen n runRandIO :: Rand a -> IO a runRandIO r = randomIO >>= (\rnd -> return $ runRand rnd r) getRandoms :: (Random a) => Int -> Rand [a] getRandoms n = mapM (\_ -> getRandom) [1..n] ```

Original source