13

如果 F# 返回第一个(从左到右,如果有的话)函数在列表元素上的成功应用,则有一个标准的tryPick函数。我希望在 Haskell 中有一个像这样的标准函数。我尝试了 Hoogle 并没有找到任何东西。

我是 Haskell 的新手,我不确定正确的做法是什么。你会这样做吗:

tryPick:: (a -> Maybe b) -> [a] -> Maybe b
tryPick try xs = case Maybe.mapMaybe try xs of
    [] -> Nothing
    (x:_) -> Just x

?

4

4 回答 4

16

你要:

tryPick :: (a -> Maybe b) -> [a] -> Maybe b
tryPick f as = msum (map f as)

我将解释这是如何工作的。

map f as生成Maybe要尝试的可能操作列表:

map f as :: [Maybe b]

msum按顺序尝试它们,直到一个成功(将值返回为 a Just)或它们都失败(返回 a Nothing)。例如:

> msum [Nothing, Just 2, Just 3, Nothing]
Just 2
> msum [Nothing, Nothing]
Nothing

注意msum's 的类型更通用,所以我们可以将签名泛化为:

tryPick :: (MonadPlus m) => (a -> m b) -> [a] -> m b

这现在适用于任何MonadPlus. 发现它对其他MonadPlus类型的作用很有趣。

于 2013-10-28T21:22:14.747 回答
14

中的listToMaybe功能Data.Maybe看起来不错:

tryPick f = listToMaybe . mapMaybe f
于 2013-10-28T19:58:48.580 回答
12

它不一定是最简单的解决方案,但我觉得强调First Monoid基于解决方案很重要。我觉得是最美的

import Data.Monoid
import Data.Foldable (Foldable, foldMap)

tryPick :: (a -> Maybe b) -> [a] -> Maybe b
tryPick f = getFirst . foldMap (First . f)     -- this is just `foldMap f`
                                               -- with the "firsty" Maybe Monoid

这也可以立即推广到任何Foldable具有完全相同代码的人

tryPick :: Foldable t => (a -> Maybe b) -> t a -> Maybe b

Foldable实例提供了使用 s 将所有元素“粉碎”在一起的方法MonoidFirst Monoid定义为

newtype First a = First { getFirst :: Maybe a }

是选择“第一个”或“最左边”Maybe的操作的特化。mappendJust

因此,将它们放在一起,getFirst . foldMap (First . f)计算您(a -> Maybe b)对 中所有as 的函数,然后将结果与“第一个”获胜[a]的规则一起粉碎。Just

于 2013-10-28T19:58:56.530 回答
8

我来晚了一点,但这是 J. Abrahamson 的答案的一个变体,它使用了ala'中 Conor McBride 的可爱功能:newtype

import Control.Newtype (ala')
import Data.Foldable (Foldable, foldMap)
import Data.Monoid (First(..))

tryPick :: (Foldable t) => (a -> Maybe b) -> t a -> Maybe b
tryPick = ala' First foldMap

这可能看起来有点神秘,但我发现它将“收集容器” First(美丽的。根据我的经验,它是创建漂亮代码的好工具,我想插入它。谢谢,康纳!foldMapa -> Maybe bnewtypeala

于 2013-11-08T11:07:34.990 回答