将随机数生成添加到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基本上是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 

是脱糖后做记号

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是足够聪明的减少,这是更进一步,所以状态刚过通,而无需创建中间对(但我不知道,应该使用单子法律来证明):

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

这是你从

lift do x <- baseAction1 
     y <- baseAction2 
     return $ f x y 
得到什么