Monad Transformer(RandT)中的多个独立ST / State monad ...复杂的包装/展开

时间:2013-07-07 21:08:28

标签: haskell monads monad-transformers

学习如何更直观地掌握单子和变形金刚;很多看似显而易见的事情对我来说仍然很棘手哈哈。

所以我的计算存在于Rand monad中,但里面,还有另一个“子计算”(或多个)存在于{{1}内} monad(或ST monad,重要的是...... State仅用于表现,但我认为ST在这种情况下同样适用。)

整个计算不需要在State monad中...并且这个子计算将被调用多次,具有不同的起始状态,所以我不想强迫整个事情进入一个ST(除非这是惯用的方式)。

没有随机性,结构如下:

ST

基本上工作得很好,main = print mainComp mainComp :: Int mainComp = otherComp + (subComp 1) + (subComp 2) subComp :: Int -> Int subComp n = runST $ do -- generate state based on n -- ... replicateM_ 100 mutateState -- ... -- eventually returns an ST s Int mutateState :: ST s () mutateState = -- ... mainComp都有完整的引用透明度。

这就是我到目前为止使用subComp -

的方法
Rand

如果我想使用随机种子和main = (evalRandIO mainComp) >>= print mainComp :: (RandomGen g) => Rand g Int mainComp = do subResultA <- subComp 1 subResultB <- subComp 2 return $ otherComp + subResultA + subResultB subComp :: (RandomGen g) => Int -> Rand g Int subComp = return $ runST $ do -- is this ok to just throw in return? -- generate state based on n -- ... replicateM_ 100 mutateState -- ... -- eventually returns an ST s Int (??) mutateState :: ?? mutateState = ?? monad,那么mutateState应该是什么类型?我想我可能想要使用Rand的返回类型,但是如何使RandT g (ST s) ()runST中预期的类型符合要求?

1 个答案:

答案 0 :(得分:1)

使用monad变换器,您可以按照添加它们的相反顺序“剥离”图层。因此,如果您的某些类型为RandT g (ST s) (),则首先要使用evalRandTrunRandT删除RandT,然后再调用runST

以下是合并RandTST的简单示例:

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)

修改:这是一个扩展版本,现在有一个函数runSTBelowRand,可让您在RandT StdGen (ST s) a次计算中嵌入Rand StdGen a次计算。该函数使用getSplit来分割全局计算的种子,并将新种子提供给子计算。

{-# 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)