像(Monad m) => (s -> a -> m (s, b))
这样的函数产生一个新状态和一个基于先前状态和当前值的新值是非常频繁的。
我们可以使用不同的方法来实现a
列表的遍历,以产生m [b]
给定函数f :: s -> a -> m (s, b)
Control.Monad.foldM
但代码不是特别好traverse
和StateT (WriterT m)
monad,这有点好有没有很好地利用现有的库来分解f
的“输出行为”的“状态定义的行为”,并在几个组合器中获得所需的遍历?
普通的StateT
就足够了,
foo :: Monad m => (s -> a -> m (s, b)) -> s -> [a] -> m [b]
foo g = flip (evalStateT . mapM (StateT . f))
where
f a s = liftM swap $ g s a
swap (a,b) = (b,a) -- or import Data.Tuple
如果你必须使用你的确切类型而不是更自然的类型flip
,f
和a -> s -> m (b, s)
使这些部分合适。
直到newtype废话,我们有:
traverse @[] @(StateT s m) :: (a -> s -> m (a, s)) -> [a] -> s -> m ([b], s)
根据Will Ness的回答,因为我有机会在我的代码中重新排列参数,我可以得到以下结果
foldAccumulate :: (Monad m) => (a -> s -> m (b, s)) -> [a] -> s -> m [b]
foldAccumulate f = evalStateT . traverse (StateT . f)
这确实是一个traverse
与适当的StateT m
monad,并且没有必要写任何东西,我不知道为什么我没有看到:-)。谢谢!