只是学习如何对单子和变压器有更深入的直观了解;对我来说,很多看似显而易见的事情仍然有些棘手,哈哈。

因此,我有一个计算驻留在Rand monad中,但在其中,还有另一个“子计算”(或多个)存在于ST monad(或State monad)中,这很重要。 .. ST仅用于性能,但我认为State在这种情况下同样有效。

整个计算不需要在ST monad中...并且此子计算将以不同的起始状态多次调用,因此我不想将整个内容强制为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 = -- ...


基本上,一切工作都很好,并且mainCompsubComp中具有完全的引用透明性。

到目前为止,这就是我使用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 = ??


如果我想在其中使用随机种子和mutateState单子,那么Rand应该是什么类型?我想我可能想使用RandT g (ST s) ()的返回类型,但是如何使它与runST中的subComp期望的类型相符?

最佳答案

使用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)

10-08 12:39