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

我目前正在研究
Haskell中的一些事务性内存基准测试,并希望能够在事务中使用随机数.我目前正在使用
here中的Random monad / monad变换器.在下面的例子中,我有一个包含整数和一个事务的数组,它随机选择数组中的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

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

最佳答案 STM是一个基础monad,从原子角度思考,这是目前的STM a – >如果我们有STMT,IO应该看起来像.

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

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基本上是StateT的升力:

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'

StateT和runStateT取消:

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
点赞