首页 文章

将随机数生成添加到Haskell中的STM monad

提问于
浏览
4

我目前正在研究Haskell中的一些事务性内存基准测试,并希望能够在事务中使用随机数 . 我目前正在使用here中的Random monad / monad变换器 . 在下面的示例中,我有一个包含整数的TVar数组和一个随机选择数组中10个tvar的事务,例如:

tvars :: STM (TArray Int Int)
tvars = newArray (0, numTVars) 0

write :: Int -> RandT StdGen STM [Int]
write 0 = return []
write i = do
    tvars <- lift tvars
    rn <- getRandomR (0, numTVars)
    temp <- lift $ readArray tvars rn
    lift $ writeArray tvars rn (temp + 1)
    rands <- write (i-1)
    lift $ return $ rn : rands

我想我的问题是"Is this the best way to go about doing this?"看起来更自然/更有效的方式,即将随机monad提升到STM monad中 . 每个事务执行很多STM操作,并且很少有随机操作 . 我会假设每个_515327都会增加一些开销 . 仅仅随机计算并单独留下STM计算会不会更有效率?这样做是否安全?似乎定义一个STM monad转换器会打破我们用STM monad获得的漂亮的静态分离属性(即我们可以将IO提升到STM monad,但是如果事务中止,我们必须担心撤消IO操作问题数量) . 我对monad变压器的了解非常有限 . 关于使用变压器的性能和相对开销的简要说明将非常受欢迎 .

1 回答

  • 2

    STM是一个基础monad,想想 atomically ,当前 STM a -> IO a 应该是什么样的,如果我们有 STMT .

    我对你的特定问题的解决方案很少 . 更简单的可能是重新安排代码:

    write :: Int -> RandT StdGen STM [Int]
    write n = do
       -- random list of indexes, so you don't need to interleave random and stm code at all
       rn <- getRandomRs (0, numTVars) 
       lift $ go rn
       where go []     = return []
             go (i:is) = do tvars <- tvars -- this is redundant, could be taken out of the loop
                            temp <-  readArray tvars i
                            writeArray tvars i (temp + 1)
                            rands <- go is
                            return $ i : rands
    

    然而 RandT 基本上 StateTlift

    instance MonadTrans (StateT s) where
        lift m = StateT $ \ s -> do
            a <- m
            return (a, s)
    

    所以形式代码:

    do x <- lift baseAction1
       y <- lift baseAction2
       return $ f x y
    

    将会

    do x <- StateT $ \s -> do { a <- baseAction1; return (a, s) }
       y <- StateT $ \s -> do { a <- baseAction2; return (a, s) }
       return $ f x y
    

    在desugaring做符号之后

    StateT (\s -> do { a <- baseAction1; return (a, s) }) >>= \ x ->
    StateT (\s -> do { a <- baseAction2; return (a, s) }) >>= \ y ->
    return $ f x y
    

    首先内联 >>=

    StateT $ \s -> do
      ~(a, s') <- runStateT (StateT (\s -> do { a <- baseAction1; return (a, s) })) s
      runStateT ((\ x -> StateT (\s -> do { a <- baseAction2; return (a, s) }) >>= \ y -> return $ f x y) a) s'
    

    StateTrunStateT 取消:

    StateT $ \s -> do
      ~(x, s') <- do { a <- baseAction1; return (a, s) }))
      runStateT ((\ x -> StateT (\s -> do { a <- baseAction2; return (a, s) }) >>= \ y -> return $ f x y) x) s'
    

    经过几次内联/缩小步骤:

    StateT $ \s -> do
      ~(x, s') <- do { a <- baseAction1; return (a, s) }))
      ~(y, s'') <- do { a <- baseAction2; return (a, s') }))
      return (f x y, s'')
    

    可能GHC足够聪明,可以进一步降低这一点,所以状态只是在没有创建中间对的情况下通过(但我不确定,应该使用monad法则来证明这一点):

    StateT $ \s -> do
       x <- baseAction1
       y <- baseAction2
       return (f x y, s)
    

    这是你得到的

    lift do x <- baseAction1
            y <- baseAction2
            return $ f x y
    

相关问题