将折叠器提升到monad

时间:2013-06-11 23:32:19

标签: haskell monads fold

我有函数smallStep :: Command -> Data -> Either StepError Data,我希望bigStep :: [Command] -> Data -> Either StepError Data使用它,并使用以下语义:

bigStep []   data   = Right data
bigStep c:cs data   = do
               data' <- bigStep cs data
               smallStep c data'

这没有什么令人费解的,但如果smallStep的类型为Command -> Data -> Data,我会将bigStep简称为bigStep commands data = foldr data smallStep commands

当然,我也想在这里使用foldr。我该怎么做呢? foldM被取消foldl,倒车清单听起来并不是一个非常好的主意。

1 个答案:

答案 0 :(得分:7)

通常,在资源使用方面,左侧折叠不会比右侧折叠更好或更差。但是,我认为[Command]应该是有序命令的列表,这些命令要按照提供的顺序一个接一个地执行。如果是这种情况,可能最容易向后构建这些列表(而不是反转它们 - 对于长列表来说这确实是一项代价高昂的操作)。如果你不想这样做,这里是一般的monadic右折:

foldrM :: Monad m => (a -> b -> m b) -> b -> [a] -> m b
foldrM f d []     = return d
foldrM f d (x:xs) = (\z -> f x z) =<< foldrM f d xs

请注意以下所有类型:

foldl :: (a -> b -> a) -> a -> [b] -> a
foldM :: Monad m => (a -> b -> m a) -> a -> [b] -> m a

foldr :: (a -> b -> b) -> b -> [a] -> b
foldrM :: Monad m => (a -> b -> m b) -> b -> [a] -> m b

我们可以推断foldrM确实是一个正确的折叠。

但是,如果你需要fold一个非常大的列表,上面的两个monadic折叠都是懒惰的,只有在最后一个函数应用程序排序后才会开始评估。