让我们采取两个功能:
f :: a -> Maybe b
g :: b -> Maybe c
函数>>=
的工作方式是f >>= g
只有在g
f
时才会执行Nothing
f
。换句话说,它需要g
和f :: a -> Maybe b
g :: a -> Maybe b
planb :: (a -> Maybe b) -> (a -> Maybe b) -> (a -> Maybe b)
planb f g = \x -> case f x of
Nothing -> g x
res -> res
才能成功产生任何结果。
我正在实现一个解析器,并意识到我的词法分析器会从中获益。那就是:
f
表示尝试g
,如果失败,请尝试planb
作为备用计划。使用词法分析器意味着尝试将令牌类型与当前输入匹配,如果失败,请尝试匹配另一个令牌类型(最终将为所有令牌类型链接)。
搜索Hoogle并没有导致任何此类功能,但对我来说这样的功能似乎在许多地方都很有用!
我的问题是,我是否应该使用Monad
的变体?如果没有,我会做一些非凡的事情,有更好的方法来实现我想要的目标吗?
P.S。我考虑过这个函数一般对Maybe
是否有意义,但对{{1}}之外的其他人来说并没有那么大的意义。
答案 0 :(得分:20)
Alternative
类型类正是这样做的,它与MonadPlus
非常相似,但可能更为一般。
import Control.Applicative
-- most general form
planb :: (Applicative g, Alternative f) => g (f a) -> g (f a) -> g (f a)
planb = liftA2 (<|>)
-- specialized to (->) and Maybe
planb' :: (a -> Maybe b) -> (a -> Maybe b) -> (a -> Maybe b)
planb' = planb
-- equivalent to planb' (and planb) but without the fancy combinators
planb'' :: (a -> Maybe b) -> (a -> Maybe b) -> a -> Maybe b
planb'' f g x = f x <|> g x
将其插入一个简单的测试用例:
test :: Maybe Int
test = do
a <- planb' (const Nothing) id (Just 1)
b <- planb' id id (Just 1)
c <- planb' id (const Nothing) (Just 1)
return $ a + b + c
生成预期结果:
*Main> test
Just 3
答案 1 :(得分:6)
请注意,您的planb
功能实际上只需要对Maybe
值进行操作;调用函数来生成它们可以被考虑在内。
planb :: Maybe a -> Maybe a -> Maybe a
planb Nothing b = b
planb a _ = a
您可以将其称为planb (f x) (g x)
,以获得Maybe
结果。
考虑到这一点,请查看MonadPlus
class(在评论中Franky建议):
planb = mplus
您可能还对msum
感兴趣,Maybe
会获取Nothing
值列表并返回不是matchSomehow :: [a -> Maybe b] -> a -> Maybe b
matchSomehow fs a = msum $ map ($a) fs
的第一个值(如果有)。这是一个方便的功能:
{{1}}