实现采用 [Maybe a] 和 returns Maybe [a] 的函数的更好方法

better way to implement function that takes [Maybe a] and returns Maybe [a]

我需要一个函数,它将 [Maybe a] 列表作为输入,获取每个值,对其进行处理,然后 return Maybe [a]。如果输入列表没有任何内容,我想 return Nothing。

func [Just 1,Just 2,Just 3,Just 4,Just 5] => this returns Just [1,2,3,4,5]
func [Just 1,Nothing,Just 3,Just 4,Just 5] => this returns Nothing

这是我写的

func mlist = if elem Nothing mlist
             then Nothing
             else Just $ map (\(Just e) -> e) mlist

它有效,但我想知道我是否可以做得更好。我不喜欢我首先执行 elem Nothing mlist 然后再次映射 mlist 的部分。

此功能已与 sequence :: Monad m => [m a] -> m [a] 函数一起存在:

Prelude> import Control.Monad
Prelude Control.Monad> sequence [Just 3]
Just [3]
Prelude Control.Monad> sequence [Just 3, Nothing]
Nothing
Prelude Control.Monad> sequence [Just 3, Just 2]
Just [3,2]
Prelude Control.Monad> sequence [Just 1,Just 2,Just 3,Just 4,Just 5]
Just [1,2,3,4,5]
Prelude Control.Monad> sequence [Just 1,Nothing,Just 3,Just 4,Just 5]
Nothing

本质上这只是一个 mapM id :: (Monad m, Traversable t) => t (m a) -> m (t a),因为例如对于 3 列表,它等于:

-- special case for 3 elements to demonstrate how it works
func3 [a, b, c] = do
    ya <- a
    yb <- b
    yc <- c
    return [ya, yb, yc]

或者像这样:

func3 [a, b, c] = a >>= \ya -> b >>= \yb -> c >>= yc -> return [ya, yb, yc]

(我在这里使用了一个特例,因为 mapM 引入了一些额外的函数使其更难理解)

因为 MaybeMonad Maybe 的实现方式如下:

instance Monad Maybe where
    return = Just
    Nothing >>= _ = Nothing
    (Just x) >>= f = f x

所以这意味着从元素之一(abc)是 Nothing 的那一刻起,结果将是 Nothing,如果所有值都是 Justs,我们将 "collect" 这些与 lambda 表达式,并最终产生一个包含元素的列表。

您可以将列表 [] 视为 Maybe 的概括(其中 Nothing 是空列表,而 Just 是单例列表),并且可以观察到相同的行为:

Prelude Control.Monad> sequence [[1,2], [4,3]]
[[1,4],[1,3],[2,4],[2,3]]
Prelude Control.Monad> sequence [[1,2], [4,3], []]
[]

此处 sequence 将生成一个 叉积 ,但是如果提供我们应用叉积的集合之一的元素的列表之一是空,结果也为空