Сначала вы должны превратить [Maybe a]
в Maybe [a]
со всеми элементами Just
(получая Nothing
, если любой из них равен Nothing
).
Это можно сделать, используя sequence , используя экземпляр Maybe's Monad:
GHCi> sequence [Just 1, Just 2]
Just [1,2]
GHCi> sequence [Just 1, Just 2, Nothing]
Nothing
Определение последовательности эквивалентно следующему:
sequence [] = return []
sequence (m:ms) = do
x <- m
xs <- sequence ms
return (x:xs)
Таким образом, мы можем расширить последний пример следующим образом:
do x <- Just 1
xs <- do
y <- Just 2
ys <- do
z <- Nothing
zs <- return []
return (z:zs)
return (y:ys)
return (x:xs)
Используя выражение do-notation законов монады , мы можем переписать это следующим образом:
do x <- Just 1
y <- Just 2
z <- Nothing
return [x, y, z]
Если вы знаете, как работает монада Maybe, теперь вы должны понимать, как работает sequence
для достижения желаемого поведения. :)
Затем вы можете составить это с помощью foldr
, используя (<$>)
(из Control.Applicative ; эквивалентно, fmap
или liftM
), чтобы свернуть вашу двоичную функцию по списку:
GHCi> foldl' (+) 0 <$> sequence [Just 1, Just 2]
Just 3
Конечно, вы можете использовать любой фолд, например foldr
, foldl1
и т. Д.
В качестве дополнения, если вы хотите, чтобы результат был Nothing
, когда список пуст, и, таким образом, вы могли опустить нулевое значение сгиба, не беспокоясь об ошибках в пустых списках, тогда вы можете использовать эту функцию сгиба :
mfoldl1' :: (MonadPlus m) => (a -> a -> a) -> [a] -> m a
mfoldl1' _ [] = mzero
mfoldl1' f (x:xs) = return $ foldl' f x xs
и аналогично для foldr
, foldl
и т. Д. Вам нужно будет импортировать Control.Monad для этого.
Однако это нужно использовать немного по-другому:
GHCi> mfoldl1' (+) =<< sequence [Just 1, Just 2]
Just 3
или
GHCi> sequence [Just 1, Just 2] >>= mfoldl1' (+)
Just 3
Это потому, что, в отличие от других сгибов, тип результата выглядит как m a
вместо a
; это привязка , а не карта .