为了从不同的方向解决您的问题,我认为您不需要一个处理两个
Maybe [a]
的函数。请耐心听我解释:
基本上,您想要执行的操作是对两个列表进行操作以生成一个新的列表,例如:
yourFunction :: [a] -> [a] -> [a]
yourFunction a b = ...
没问题,您可以并且应该像这样编写yourFunction
。事实上,您拥有的数据是Maybe [a]
,它捕获了一些附加的辅助信息:创建输入列表的操作可能已经失败。下一步是将yourFunction
与辅助信息链接在一起。这正是do
符号的目的,将纯操作(如yourFunction
)与上下文(即创建您的输入列表之一可能已失败的事实)混合在一起:
playWithMaybe :: Maybe [a] -> Maybe [a] -> Maybe [a]
playWithMaybe maybeA maybeB =
do a <- maybeA
b <- maybeB
Just (yourFunction a b)
但是,事实证明您可能需要使用其他类型的上下文进行工作(例如,不使用Maybe
简单地捕获“发生了一些糟糕的事情”,我们可以使用Either
来捕获“发生了一些糟糕的事情,并且这是对所发生事情的描述)。回顾一下playWithMaybe
函数,"Maybe-ness
"只出现在最后一行的Just
中。原来Haskell提供了一个通用函数pure
来将纯值(例如从yourFunction
获得的值)包装为最小的上下文:
playWithMaybe' :: Maybe [a] -> Maybe [a] -> Maybe [a]
playWithMaybe' maybeA maybeB =
do a <- maybeA
b <- maybeB
pure (yourFunction a b)
然而,Haskell还有一种通用类型来抽象上下文的概念,即Monad。这使得我们的函数变得更加通用:
playWithMonad :: Monad m => m [a] -> m [a] -> m [a]
playWithMonad mA mB =
do a <- mA
b <- mB
pure (yourFunction a b)
现在我们有一个非常通用的东西,事实证明它非常通用,它已经在标准库中了!(这变得相当微妙,所以如果还不太明白也不用担心。)
import Control.Applicative
play :: Monad m => m [a] -> m [a] -> m [a]
play mA mB = liftA2 yourFunction mA mB
甚至可以
import Control.Applicative
play' :: Monad m => m [a] -> m [a] -> m [a]
play' = liftA2 yourFunction
我为什么突然从Monad转向Applicative? Applicative与Monad类似,但更加通用,因此如果可以选择,通常最好使用Applicative(就像之前选择使用pure
而不是return
一样)。为了更完整的解释,强烈推荐阅读《学习 Haskell》(http://learnyouahaskell.com/chapters)的第11和12章。注意-一定要先阅读第11章!只有在掌握Functor和Applicative后,Monad才有意义。
Maybe
类型,可能(或可能不)包含一个列表。—— 我不清楚你试图做什么。 - leftaroundabout[]
和x : xs
),Maybe
也有两个基本的构造函数(Nothing
和Just x
),你可以对其进行模式匹配。 - melpomeneMaybe [a]
(你可以做的事情不多)。也许需要更多上下文。 - n. m.Maybe [a]
转换为[a]
,您可以使用以下函数:foo Nothing = []; foo (Just xs) = xs
。或者导入Data.Maybe
并直接使用fromMaybe []
。 - melpomene