在 Haskell 中将随机数生成添加到 STM monad
Adding random number generation to the STM monad in Haskell
我目前正在 Haskell 中进行一些事务性内存基准测试,并希望能够在事务中使用随机数。我目前正在使用来自 here 的随机 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?" 似乎更 natural/efficient 反过来,即将随机 monad 提升到 STM monad。每笔交易做大量的 STM 操作,很少有随机操作。我假设每个 lift
都会增加一些开销。仅 lift
随机计算而单独留下 STM 计算不是更有效吗?这样做安全吗?似乎定义一个 STM monad 转换器会破坏我们通过 STM monad 获得的良好的静态分离属性(即我们可以将 IO 提升到 STM monad,但是如果事务中止呈现题数)。我对 monad 转换器的了解非常有限。非常感谢关于使用变压器的性能和相对开销的简要说明。
STM 是一个基础 monad,如果我们有 STMT
.
,想想 atomically
,目前 STM a -> IO a
应该是什么样子
对于您的特定问题,我几乎没有什么解决方案。比较简单的大概是重新整理一下代码:
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
和 lift
:
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
这是脱糖后的do符号
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
我目前正在 Haskell 中进行一些事务性内存基准测试,并希望能够在事务中使用随机数。我目前正在使用来自 here 的随机 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?" 似乎更 natural/efficient 反过来,即将随机 monad 提升到 STM monad。每笔交易做大量的 STM 操作,很少有随机操作。我假设每个 lift
都会增加一些开销。仅 lift
随机计算而单独留下 STM 计算不是更有效吗?这样做安全吗?似乎定义一个 STM monad 转换器会破坏我们通过 STM monad 获得的良好的静态分离属性(即我们可以将 IO 提升到 STM monad,但是如果事务中止呈现题数)。我对 monad 转换器的了解非常有限。非常感谢关于使用变压器的性能和相对开销的简要说明。
STM 是一个基础 monad,如果我们有 STMT
.
atomically
,目前 STM a -> IO a
应该是什么样子
对于您的特定问题,我几乎没有什么解决方案。比较简单的大概是重新整理一下代码:
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
和 lift
:
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
这是脱糖后的do符号
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