With monad transformers, you 'peel off' layers in the inverse order in which you add them. So, if you have something of type RandT g (ST s) ()
, you start by eliminating the RandT using evalRandT
or runRandT
, and only then calling runST
.
Here's a simple example of combining RandT
and ST
:
import Data.STRef
import System.Random
import Control.Monad
import Control.Monad.Trans
import Control.Monad.ST
import Control.Monad.Random
import Control.Monad.Random.Class
stNrand :: RandT StdGen (ST s) Int
stNrand = do
ref <- lift $ newSTRef 0
i <- getRandomR (0,10)
lift $ writeSTRef ref i
lift $ readSTRef ref
main :: IO ()
main = putStrLn . show $ runST $ evalRandT stNrand (mkStdGen 77)
Edit: Here's an expanded version, now with a function runSTBelowRand
that lets you embed RandT StdGen (ST s) a
computations in Rand StdGen a
computations. The function uses getSplit to split the seed of the global computation, and feed the new seed to the sub-computation.
{-# LANGUAGE RankNTypes #-}
import Data.STRef
import System.Random
import Control.Monad
import Control.Monad.Trans
import Control.Monad.ST
import Control.Monad.Random
import Control.Monad.Random.Class
stNrand :: RandT StdGen (ST s) Int
stNrand = do
ref <- lift $ newSTRef 0
i <- getRandomR (0,10)
lift $ writeSTRef ref i
lift $ readSTRef ref
runSTBelowRand :: (forall s. RandT StdGen (ST s) a) -> Rand StdGen a
runSTBelowRand r = do
splittedSeed <- getSplit
return $ runST $ evalRandT r splittedSeed
globalRand :: Rand StdGen (Int,Int)
globalRand = do
i1 <- runSTBelowRand stNrand
-- possibly non-ST stuff here
i2 <- runSTBelowRand stNrand
return (i1,i2)
main :: IO ()
main = putStrLn . show $ evalRand globalRand (mkStdGen 77)