学习如何更直观地掌握单子和变形金刚;很多看似显而易见的事情对我来说仍然很棘手哈哈。
所以我的计算存在于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
中预期的类型符合要求?
答案 0 :(得分:1)
使用monad变换器,您可以按照添加它们的相反顺序“剥离”图层。因此,如果您的某些类型为RandT g (ST s) ()
,则首先要使用evalRandT
或runRandT
删除RandT,然后再调用runST
。
以下是合并RandT
和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)
修改:这是一个扩展版本,现在有一个函数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)