Let's take two functions:
f :: a -> Maybe b
g :: b -> Maybe c
The function >>=
would work in such a way that f >>= g
would execute g
with the result of f
only if it's not Nothing
. In other words, it requires both f
and g
to succeed to produce any result.
I'm implementing a parser and realized that my lexer would benefit from somewhat the opposite of this. That is:
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
which means try f
and if it fails, try g
as a backup plan. With a lexer it means try to match a token type with the current input and if it failed, try to match another token type (which would eventually be chained for all token types).
Searching Hoogle didn't result in any such function, but to me such a function seems to be useful in many places!
My question is thus, whether a variant of planb
exists already that I should be using? If not, am I doing something extraordinary and there is a better way to achieve what I want?
P.S. I thought about whether such a function makes sense for Monad
s in general, but it doesn't make that much sense to me outside Maybe
and perhaps a few others.
The Alternative
typeclass does precisely this, it is quite similar to MonadPlus
but perhaps a bit more general.
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
Plugging this in to a simple test case:
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
Generates the expected result:
*Main> test
Just 3
Note that your planb
function really only needs to operate on Maybe
values; calling the functions to produce them can be factored out.
planb :: Maybe a -> Maybe a -> Maybe a
planb Nothing b = b
planb a _ = a
And you'd call it like planb (f x) (g x)
to get a Maybe
result.
With that in mind, take a look at the MonadPlus
class (as suggested by Franky in a comment):
planb = mplus
You might also be interested in msum
, which takes a list of Maybe
values and returns the first one (if any) that isn't Nothing
. Here's a handy function:
matchSomehow :: [a -> Maybe b] -> a -> Maybe b
matchSomehow fs a = msum $ map ($a) fs
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With