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)
如何实现这一目标?
答案 0 :(得分:4)
最简单的方法是使用msum
(mplus
的列表版本)和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
实例不是您想要的(“忽略Nothing
,mappend
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 a
和Monoid 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)
所以现在可以理解上面的代码:
First
- 包装所有输入函数,将它们从a -> Maybe b
转移到a -> First b
,这是相同的,但具有不同的Maybe
{{1实例。
Monoid
函数列表,它使用我刚刚提到的mconcat
实例。创建的列表中的所有函数都应用于Monoid b => (a -> b)
,为您留下x
的列表,然后再次连接,就像在(1)中一样。
再次从First b
包装中提取生成的Maybe
值。