将随机数生成添加到Haskell中的STM monad
问题描述:
我目前正在研究Haskell中的一些事务内存基准,并希望能够在事务中使用随机数。我目前使用here的Random monad/monad变压器。在下面的例子,我有一个包含整数TVars的阵列和阵列递增中随机选择10个tvars事务,如:将随机数生成添加到Haskell中的STM monad
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操作,并且很少有随机操作。我会假设每个lift
都会增加一些开销。只有随机计算才能更有效率,并且只剩下STM计算?这是否安全?看起来,定义一个STM monad变换器会破坏我们用STM monad获得的良好静态分离属性(即,我们可以将IO提升到STM monad中,但是如果事务中止会导致出现问题的数量)。我对monad变形金刚的了解相当有限。有关使用变压器的性能和相关开销的简要说明将不胜感激。
答
STM是一个基本单子,想一下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
基本上是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
是脱糖后做记号
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是足够聪明的减少,这是更进一步,所以状态刚过通,而无需创建中间对(但我不知道,应该使用单子法律来证明):
StateT $ \s -> do
x <- baseAction1
y <- baseAction2
return (f x y, s)
这是你从
lift do x <- baseAction1
y <- baseAction2
return $ f x y
得到什么