haskellgeneratorgeneralization

Is there a way to generalise multiple generators and randoms in haskell


I'm learning Haskell. Average of large number of Dice Rolls in Haskell How shall I make the below code work with multiple dices say 8 dices) instead of repeatedly creating let rolls..3,4,5...etc...

module Dice where
import System.Random
import Data.List

dice = do
    g <- getStdGen
    b <- newStdGen
    let trials = 1000
    let rolls1 = take trials (randomRs (1,6) g :: [Int])
    let rolls2 = take trials (randomRs (1,6) b::[Int])
    let rolls = zipWith (+) rolls1 rolls2
    let average = div (foldl' (+) 0 rolls) trials
    print average

Solution

  • The System.Random module offers some basic primitive, but (in my opinion) it lacks a proper monadic interface. More precisely, it offers an IO-based monadic interface, but lacks a State-based monadic interface. The latter is however easy to define , though.

    Anyway, if one wants to stick with IO and the standard generator, one could write:

    -- (untested code)
    
    rollDie :: Int -> IO Int
    rollDie n = randomIO (1,n) -- implicitly uses the global generator
    
    rollManyDice :: Int -> Int -> IO [Int]
    rollManyDice howMany n = replicateM howMany (rollDie n)
    
    main :: IO ()
    main = do
       dice <- rollManyDice 20 6
       putStrLn $ "Here's 20 6-sides dice : " ++ show dice
    

    where replicateM executes howMany times a monadic action, collecting all the results in a list. Here the monad is IO, but it could be anything.

    This is a fine and simple approach, but the IO type above is a bit too much, preventing us to call rollDie from non-IO code. A State-based solution would not have this limination

    type R a = State StdGen a
    
    rollDie :: Int -> R Int
    rollDie n = state $ randomR (1,n) -- uses the generator inside the State monad
    
    rollManyDice :: Int -> Int -> R [Int]
    rollManyDice howMany n = replicateM howMany (rollDie n)
    
    main :: IO ()
    main = do
       g <- getStdGen
       let dice = evalState (rollManyDice 20 6) g
       putStrLn $ "Here's 20 6-sides dice : " ++ show dice