Ответ 1
Сначала вы должны превратить [Maybe a]
в Maybe [a]
со всеми элементами Just
(при условии Nothing
, если любой из них Nothing
).
Это можно сделать, используя sequence, используя экземпляр Maybe 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
; это привязка, а не карта.