使用mplus作为函数列表

时间:2014-05-20 13:43:19

标签: haskell

Using the Maybe Monad in “reverse” acfoltzer很好shows如何使用mplus。我希望有类似的效果,但将函数列表作为参数:

tryFuncs :: [a -> Maybe b] -> a -> Maybe b
...

所以像

这样的电话
tryFuncs [f, g, h] x

将成为可能并且与

相同
(f x) `mplus` (g x) `mplus` (h x)

如何实现这一目标?

2 个答案:

答案 0 :(得分:4)

最简单的方法是使用msummplus的列表版本)和map

tryFuncs fs x = msum $ map ($ x) fs

答案 1 :(得分:2)

(最后,这个解决方案与ØrjanJohansen的答案完全相同,因为Maybe s MonadPlus等同于First Monoid的行为。这是一个尽管很少应用a -> b幺半群,但容易被忽视。)

从概念上讲,您正在寻找的功能是...... mconcat

tryFuncs' :: Monoid b => [a -> Maybe b] -> a -> Maybe b
tryFuncs' = mconcat

不幸的是,Monoid的默认Maybe实例不是您想要的(“忽略Nothingmappend Just内容”),否则解决方案本来就很整洁。

First周围的Maybe包装器会为您提供“保留第一个Just”行为,以便

-- newtype First a = First (Maybe a)
tryFuncsFirst :: [a -> First b] -> a -> First b
tryFuncsFirst = mconcat

剩下的就是将Maybe换行/打开到First s。

firstify :: (a -> Maybe b) -> (a -> First b)
firstify f = First . f

firstifyList :: [a -> Maybe b] -> [a -> First b]
firstifyList = map firstify

getFirst :: First a -> Maybe a -- Defined in Data.Monoid

所以现在你可以通过wrap-mconcat-unwrapping,

来恢复你想要的功能
[a -> Maybe b] -> a -> Maybe b
tryFuncs fs x = getFirst (mconcat (firstifyList xs) x)

但这是如何运作的?嗯,这里有两个幺半群,First aMonoid b => (a -> b),后者是神奇发生的地方。要使用<> mappend

,请将实例拼写一点
(a <> b) x = a x <> b c
-- and therefore
mconcat [a,b,c] x = mconcat [a x, b x, c x] -- (1)

所以现在可以理解上面的代码:

  1. First - 包装所有输入函数,将它们从a -> Maybe b转移到a -> First b,这是相同的,但具有不同的Maybe {{1实例。

  2. Monoid函数列表,它使用我刚刚提到的mconcat实例。创建的列表中的所有函数都应用于Monoid b => (a -> b),为您留下x的列表,然后再次连接,就像在(1)中一样。

  3. 再次从First b包装中提取生成的Maybe值。