首页 > 解决方案 > 也许单子和一个列表

问题描述

好的,所以我正在尝试学习如何使用单子,从可能开始。我想出了一个例子,我无法弄清楚如何以一种很好的方式应用它,所以我希望其他人可以:

我有一个包含一堆值的列表。根据这些值,我的函数应该返回列表本身,或者返回 Nothing。换句话说,我想做一种过滤器,但命中的结果是函数失败。

我能想到的唯一方法是使用过滤器,然后比较我得到的列表的大小为零。有没有更好的办法?

标签: haskellmonads

解决方案


这看起来很适合traverse

traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)

这有点拗口,所以让我们专门针对您的用例,使用列表和Maybe

GHCi> :set -XTypeApplications
GHCi> :t traverse @[] @Maybe
traverse @[] @Maybe :: (a -> Maybe b) -> [a] -> Maybe [b]

它的工作原理是这样的:你给它一个a -> Maybe b函数,它被应用于列表的所有元素,就像这样fmap做一样。扭曲的是,这些值随后以一种仅在没有任何sMaybe b时才为您提供修改后的列表的方式组合;Nothing否则,总体结果为Nothing。这就像手套一样符合您的要求:

noneOrNothing :: (a -> Bool) -> [a] -> Maybe [a]
noneOrNothing p = traverse (\x -> if p x then Nothing else Just x)

allOrNothing本来是一个更悦耳的名字,但我不得不根据你的描述翻转测试。)

关于TraversableApplicative类,我们可能会讨论很多事情。现在,我会多谈一点Applicative,以防你还没有遇到它。Applicative是 的超类,Monad具有两个基本方法:pure,与 相同return,并且(<*>)(>>=)例如Maybe...

GHCi> :t (>>=) @Maybe
(>>=) @Maybe :: Maybe a -> (a -> Maybe b) -> Maybe b
GHCi> :t (<*>) @Maybe
(<*>) @Maybe :: Maybe (a -> b) -> Maybe a -> Maybe b

...我们可以这样描述差异: in mx >>= f, ifmx是一个Just-value,(>>=)到达它的内部以应用f并产生一个结果,根据里面的内容mx,结果将是一个Just-value 或 a Nothingmf <*> mx但是,在 if和mfare mx-valuesJust中,您可以保证获得一个Just值,该值将保存将函数 frommf应用于值 from的结果mx。(顺便说一句:如果mf或是mx会发生什么Nothing?)

traverse涉及Applicative因为我在开头提到的值的组合(在您的示例中,将多个Maybe a值转换为 a Maybe [a])是使用(<*>). 由于您的问题最初是关于 monads,值得注意的是可以定义traverseusingMonad而不是Applicative. 这种变体的名称为mapM

mapM :: (Traversable t, Monad m) => (a -> m b) -> t a -> m (t b)

我们更喜欢它traversemapM因为它更通用——如上所述,Applicative它是Monad.

最后,您对这是“一种过滤器”的直觉很有意义。特别是,一种思考方式Maybe a是,当您选择布尔值并将类型的值附加aTrue. 从这个有利的角度来看,它(<*>)可以作为&&这些奇怪的布尔值的一个,如果你碰巧提供了它们中的两个,它会结合附加的值(参见DarthFennec 的使用 实现的建议any)。一旦你习惯了Traversable,你可能会喜欢看一下and类,它们利用FilterableWitherableMaybeand之间的这种关系Bool


推荐阅读