How to execute monadic function combining state management and random generation

I have created this monadic computation that works with both state and randomness:

import Control.Monad.State
import Control.Monad.Identity
import Control.Monad.Random.Class

-- coin flip function
coinFlip :: MonadRandom m => m Bool
coinFlip = (< 0.5) <$> getRandomR (0,1 :: Double)

-- main computation I want to execute
myComputation :: (MonadState Bool m, MonadRandom m) => m Bool
myComputation = coinFlip >>= \result -> put result >> return result

I need help figuring out the proper way to execute myComputation. The goal is to make sure it returns True approximately 50% of the time when run multiple times. What’s the correct approach to run this type of computation that requires both state and random number generation?

You need a monad transformer stack with both MonadState and MonadRandom instances. Use StateT with RandT from the MonadRandom package.

Here’s how I usually do it:

import Control.Monad.State
import Control.Monad.Random
import System.Random

runMyComputation :: IO (Bool, Bool)
runMyComputation = do
  gen <- newStdGen
  return $ evalRand (runStateT myComputation False) gen

This gives you a StateT Bool (Rand StdGen) stack that handles both state management and random generation. evalRand runs the random computation with a generator, runStateT manages the state. Run this in a loop multiple times to test the distribution.

You can also flip it and use RandT on the outside with StateT inside. I like this setup better for some cases:

import Control.Monad.Random
import Control.Monad.State

runComputation :: IO Bool
runComputation = do
  gen <- getStdGen
  let (result, _) = runRand (evalStateT myComputation False) gen
  return result

Now your stack is RandT (StateT Bool Identity) instead. Both ways work the same, but this version chains multiple random computations more smoothly. Want to check the distribution? Run replicateM 1000 runComputation and count the True values.

Actually there’s a simpler approach if you don’t need to inspect the final state. Just use execStateT:

runSimple = evalRand (execStateT myComputation False) <$> newStdGen

This gives you the final state directly without the tuple. I’ve been using this pattern a lot for coin flip simulations and it works great.