STM 是一个基础 monad,想想如果我们有 ,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