Подтвердить что ты не робот

Завершение монадической складки рано

Я написал это для того, чтобы рано разорвать монадическую складку:

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

и мне интересно, есть ли более элегантный способ выразить это или что-то подобное существует в библиотеке. Конечно, есть аналогичная версия, заменяющая Maybe на Either.

Обновление... вот полное решение на основе ответа Петра Пудлака:

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
4b9b3361

Ответ 1

Это стандартная монадическая 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)

Но я бы сказал, что более удобно для функции складывания, используя MaybeT напрямую, потому что тогда он может легко завершить вычисление mzero, вместо того, чтобы манипулировать значениями Maybe, и в этом случае почти не стоит записывать для него отдельную функцию:

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

Для Either это то же самое:

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