Haskell 单子转换器(RandT)中有多个独立的ST/状态单子…复杂的包装/展开
只是学习如何更深入地直观地掌握单子和变形金刚;很多看起来显而易见的事情对我来说仍然有点棘手,哈哈 因此,我有一个位于Haskell 单子转换器(RandT)中有多个独立的ST/状态单子…复杂的包装/展开,haskell,monads,monad-transformers,Haskell,Monads,Monad Transformers,只是学习如何更深入地直观地掌握单子和变形金刚;很多看起来显而易见的事情对我来说仍然有点棘手,哈哈 因此,我有一个位于Randmonad中的计算,但在它内部,还有另一个“子计算”(或多个)位于STmonad中(或Statemonad,尽管它很重要…ST仅用于性能,但我认为State在这种情况下也同样有效) 整个计算不需要在STmonad中…并且这个子计算将以不同的开始状态被多次调用,所以我不想强制整个事情变成ST(除非这是惯用的方式) 如果没有随机性,结构如下所示: main = print ma
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 = -- ...
基本上,事情运行得很好,mainComp
和subComp
中的引用完全透明
这就是我到目前为止使用的Rand
--
main=(evalRandIO mainComp)>>=打印
mainComp::(RandomGen g)=>Rand g Int
mainComp=do
子结果Rand g Int
subComp=return$runST$do——可以直接抛出return吗?
--基于n生成状态
-- ...
replicateM_uu100酒店
-- ...
--最终返回一个sts Int(?)
房地产::??
房地产=??
如果我想在其中使用随机种子和
Rand
monad,那么mutateState
的类型应该是什么?我想我可能想使用一个返回类型RandT g(ST s)(
),但是我如何使它与子组件中runST
中所期望的类型相匹配?对于monad transformers,您可以按添加它们的相反顺序“剥离”层。因此,如果您有类型为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)
问题是,我可能在一次计算中有多个runST
,或者我的大部分计算都是在没有状态的情况下完成的,并且只有一部分在状态中。我该如何调整这一点,以便我的计算可以启动多个独立的、不同的runST
?我修改了我的问题,以反映提到的第一个要求。如果RandT是一个例子,这将很容易,因为我们可以与runST
结合使用,来变成RandT-StdGen(ST-s)Int
将计算分为Rand StdGen Int
可立即用于更广泛的计算。但是这个实例不存在,我也不知道如何声明一个,因为RandT
构造函数不是由Control.Monad.Random
导出的。这很烦人。我已经用一个变通方法扩展了我的答案。现在你可以毫无问题地嵌入多个ST计算。是的,我认为它可以工作:)谢谢:)我现在可能会使用它。然而,我有点被尴尬的“伪绑定”所困扰,你在一个较小的RandT
上使用evalRandT
,即使你已经在RandT
单子中了。单子的全部“目的”不就是你不必像这样处理传递种子,或者抽象掉这个显式的join
?
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)
{-# 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)