尽早终止monadic fold

Eri*_*ikR 13 monads haskell fold

我写了这篇文章,以便我可以提前终止monadic折叠:

myfoldM             :: (Monad m) => (a -> b -> m (Maybe a)) -> a -> [b] -> m (Maybe a)
myfoldM _ a []      =  return $ Just a
myfoldM f a (x:xs)  =  do ma <- f a x;
                          case ma of
                            Nothing -> return Nothing
                            Just a' -> myfoldM f a' xs
Run Code Online (Sandbox Code Playgroud)

我想知道是否有一种更优雅的方式来表达这个或者库中是否存在类似的东西.当然也有类似的版本替换Maybe使用Either.

更新......这是基于PetrPudlák答案的完整解决方案:

import Control.Monad (foldM,Monad,mzero)
import Control.Monad.Trans.Maybe (MaybeT(..))
import Control.Monad.Trans.Class (lift)

myfoldM' :: (Monad m) => (a -> b -> MaybeT m a) -> a -> [b] -> m (Maybe a)
myfoldM' f = (runMaybeT .) . foldM f

go :: Int -> Int -> MaybeT IO Int
go s i = do
  lift $ putStrLn $ "i = " ++ show i
  if i <= 4 then return (s+i) else mzero

test n = do
  myfoldM' go 0 [1..n] >>= print      

-- test 3 => Just 6
-- test 5 => Nothing
Run Code Online (Sandbox Code Playgroud)

Pet*_*lák 13

这只是一个标准的monadic foldM增强提前退出.这可以通过将内部计算包装到MaybeT:

import Control.Monad
import Control.Monad.Trans.Error
import Control.Monad.Trans.Maybe

myfoldM :: (Monad m) => (a -> b -> m (Maybe a)) -> a -> [b] -> m (Maybe a)
myfoldM f = (runMaybeT .) . foldM ((MaybeT .) . f)
Run Code Online (Sandbox Code Playgroud)

但是我会说MaybeT直接使用折叠函数更方便,因为它可以通过mzero而不是操纵Maybe值来轻松终止计算,在这种情况下,几乎不值得为它编写单独的函数:

myfoldM' :: (Monad m) => (a -> b -> MaybeT m a) -> a -> [b] -> m (Maybe a)
myfoldM' f = (runMaybeT .) . foldM f
Run Code Online (Sandbox Code Playgroud)

因为Either它是一样的:

myfoldM'' :: (Monad m, Error e)
          => (a -> b -> ErrorT e m a) -> a -> [b] -> m (Either e a)
myfoldM'' f = (runErrorT .) . foldM f
Run Code Online (Sandbox Code Playgroud)

  • 以有针对性的形式可能更容易阅读:``myfoldM fel = runMaybeT (foldM (\ io -&gt; (MaybeT) (fio)) el)`` (3认同)