17

2 つの関数を考えてみましょう。

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

この関数は、 そうでない場合にのみの結果で実行>>=されるように機能します。つまり、結果を出すにはとの両方が必要です。f >>= ggfNothingfg

私はパーサーを実装していますが、私のレクサーはこれとは逆の利点があることに気付きました。あれは:

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、バックアップ プランとして試行します。lexer では、トークン タイプを現在の入力と一致させようとすることを意味し、失敗した場合は別のトークン タイプと一致させようとします (最終的にはすべてのトークン タイプにチェーンされます)。

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 に答える