17

让我们使用两个函数:

f :: a -> Maybe b
g :: b -> Maybe c

该函数>>=将以这样的方式工作,仅当它不是时f >>= g才会执行g结果。换句话说,它需要两者和才能成功产生任何结果。fNothingfg

我正在实现一个解析器,并意识到我的词法分析器会从与此相反的情况中受益。那是:

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作为备用计划。使用词法分析器意味着尝试将标记类型与当前输入匹配,如果失败,则尝试匹配另一个标记类型(最终将链接到所有标记类型)。

搜索 Hoogle 并没有产生任何这样的功能,但对我来说,这样的功能似乎在很多地方都很有用!

因此,我的问题是,是否planb已经存在我应该使用的变体?如果没有,我是否正在做一些非凡的事情,并且有更好的方法来实现我想要的?


PS 我考虑过这样的功能是否对 s 一般有意义,但它对外面的我和其他一些人Monad来说并没有多大意义。Maybe

4

2 回答 2

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
于 2014-07-23T05:52:02.580 回答
6

请注意,您的planb函数实际上只需要对Maybe值进行操作;调用函数来生成它们可以被分解出来。

planb :: Maybe a -> Maybe a -> Maybe a
planb Nothing b = b
planb a _ = a

你会称它为planb (f x) (g x)得到一个Maybe结果。

考虑到这一点,看看这个MonadPlus(正如Franky在评论中建议的那样):

planb = mplus

您可能还对 感兴趣msum,它接受一个Maybe值列表并返回第一个不是 的值(如果有的话)Nothing。这是一个方便的功能:

matchSomehow :: [a -> Maybe b] -> a -> Maybe b
matchSomehow fs a = msum $ map ($a) fs
于 2014-07-23T06:16:35.287 回答