【问题标题】:Multiple independent ST/State monads within a Monad Transformer (RandT)...complicated wrapping/unwrappingMonad Transformer (RandT) 中的多个独立 ST/State monad...复杂的包装/展开
【发布时间】:2013-07-07 21:08:28
【问题描述】:

只是学习如何更直观地掌握单子和转换器;很多看起来很明显的事情对我来说仍然有点棘手哈哈。

所以我有一个位于Rand monad 中的计算,但里面,还有另一个“子计算”(或多个)位于ST monad(或State monad,不管怎样……ST 仅用于性能,但我认为 State 在这种情况下也同样有效)。

整个计算不需要在 ST monad 内......并且这个子计算将以不同的起始状态多次调用,所以我不想将整个事情强制转换为 @ 987654327@(除非这是惯用方式)。

没有随机性,结构如下:

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 = ??

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

【问题讨论】:

    标签: haskell monads monad-transformers


    【解决方案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,可让您在Rand StdGen a 计算中嵌入RandT StdGen (ST s) 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)
    

    【讨论】:

    • 问题是,我可能在一个计算中拥有多个runST——或者我的大部分计算都是在没​​有状态的情况下完成的,只有一部分在状态中。我将如何调整它以便我的计算可以启动多个独立的不同runST's?
    • 我修改了我的问题以反映提到的第一个要求
    • 如果 RandT 是 MFunctor 的一个实例,那将很容易,因为我们可以将 hoistrunST 结合使用,将 RandT StdGen (ST s) Int 子计算转换为 Rand StdGen Int 立即可用在更广泛的计算中。但是该实例不存在,我不知道如何声明一个,因为Control.Monad.Random 没有导出RandT 构造函数。这很烦人。
    • 我已经通过解决方法扩展了我的答案。现在您可以毫无问题地嵌入多个 ST 计算。
    • 是的,我认为它有效 :) 谢谢 :) 我现在可能会使用它。但是,即使您已经在 RandT monad 中,我也会对您在较小的 RandT 上使用 evalRandT 的尴尬“伪绑定”感到有些困扰。 monad 的全部“目的”不是你不必像这样处理传递种子,或者抽象出这个明确的join吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-22
    • 1970-01-01
    • 2013-05-16
    • 2011-07-01
    • 2014-05-03
    相关资源
    最近更新 更多