Adding random number generation to the STM monad in Haskell

I am currently working on some breakpoints for transactional memory in Haskell and would like to be able to use random numbers in a transaction. I am currently using a random monad / monad transformer from here . In the following example, I have a TVars array containing integers and a transaction that randomly selects 10 tvars in the array to increase, for example:

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

I think my question is: "Is this the best way to do this?" It seems like it would be more natural / efficient to go differently i.e. Raise a random monad in the STM monad. Each transaction performs many STM operations and very few random operations. I would suggest that each liftadds a certain amount of overhead. Wouldn't it be more effective just forliftrandom calculations and leave only STM calculations? Is it even safe? It seems that defining the STM monad transformer will violate the good static separation properties that we get with the STM monad (i.e., we can raise the IO to the STM monad, but then we need to worry about canceling the IO if the transaction is aborted , which represents the number of issues). My knowledge of monad transformers is quite limited. A brief explanation of the efficiency and relative costs of using a transformer would be appreciated.

+4
source share
1 answer

STM - a basic Monad, consider that atomicallythat now STM a -> IO ashould look like, if we have 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'

inlining/reduction:

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
+2

All Articles