I describe the following computation:
import Control.Monad.State
import Control.Monad.Identity
import Control.Monad.Random.Class
-- * fair coin
fair :: MonadRandom m => m Bool
fair = (\p -> p <= 0.5) <$> getRandomR (0,1 :: Double)
-- * how do i run this
bar :: (MonadState Bool m, MonadRandom m) => m Bool
bar = fair >>= \b -> put b >> return b
And I would like to know how to run bar
so that in expectation, bar
evaluates to True
half of the time.
In short:
evalRandIO $ evalStateT bar False
Let’s pick this apart. bar
needs to be able to maintain a Bool
as a state, and be able to get random values. We could nest it either way; here, I chose to layer the state atop the random, but you could do it the other way around.
evalStateT
has this type signature:
evalStateT :: Monad m => StateT s m a -> s -> m a
In words, given something that needs a state layered atop some other monad, it implements that state part and gives you that action in terms of the underlying monad. The eval
part of the name means that it will throw out the resulting state and give you just the value; there is also execStateT
that gives you the resulting state and throws out the output, and runStateT
that gives you a tuple of both. I should note that in any case, we have to give it an initial state. Your code doesn’t use the initial state, so we could just as well have used undefined
, but I used False
for the heck of it.
So now that we’ve implemented the state bit, what do we have left?
ghci> :t evalStateT bar False
evalStateT bar False :: MonadRandom m => m Bool
It wants a monad that can give it random values. Well, we have one of those. Rand
will do it. Rand
, too, has run
, eval
, and exec
variants, since it’s actually a state monad too; it holds a value of some type of class RandomGen
under the covers. Here, we don’t want to discard the state at the end, and we do want to keep the result, too, so we use the run
variant. Well, what do we have now?
ghci> :t runRand (evalStateT bar False)
runRand (evalStateT bar False) :: RandomGen g => g -> (Bool, g)
For comparison:
ghci> :t random
random :: (RandomGen g, Random a) => g -> (a, g)
So now we have a plain function that takes a random-number generator state and spits out a pair with a result and a new state, just like random
itself from System.Random
. How can we use this? Well, digging through the System.Random
module, getStdRandom
looks useful:
getStdRandom :: (StdGen -> (a, StdGen)) -> IO a
It takes a function like the one we have and turns it into an IO
action. (This makes sense that it would be IO
; it’s taking some global state (namely, the standard random number generator) and updating it.) What do we have after tacking that on?
ghci> :t getStdRandom . runRand $ evalStateT bar False
getStdRandom . runRand $ evalStateT bar False :: IO Bool
Exactly what we expect: an IO
that yields a Bool
. Run it a few times:
ghci> let barIO = getStdRandom . runRand $ evalStateT bar False
ghci> barIO
True
ghci> barIO
True
ghci> barIO
False
Random enough for me. However, as Carsten mentions in the comments, there’s a shorter way. Since this is such a common use-case, the Control.Monad.Random
module provides a shortcut, evalRandIO
, which is essentially the getStdRandom . runRand
we used above. It’s a simple replacement to end up with
evalRandIO $ evalStateT bar False