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)