Подтвердить что ты не робот

План B, или что противоположность Maybe >> =?

Возьмем две функции:

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

что означает try f, и если он не работает, попробуйте g в качестве плана резервного копирования. С помощью lexer это означает попытку сопоставить токен с текущим входом, и если он не сработал, попробуйте сопоставить другой тип токена (который в конечном итоге будет привязан для всех типов токенов).

Поиск Hoogle не привел к какой-либо такой функции, но мне такая функция кажется полезной во многих местах!

Мой вопрос, таким образом, существует ли вариант planb, который я должен использовать? Если нет, делаю ли я что-то необыкновенное, и есть лучший способ добиться того, чего я хочу?


P.S. Я подумал о том, имеет ли смысл такая функция для Monad вообще, но она не имеет для меня такого смысла вне Maybe и, возможно, нескольких других.

4b9b3361

Ответ 1

Alternative typeclass делает именно это, он очень похож на 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

Ответ 2

Обратите внимание, что ваша функция 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