How do I do conditional monadic parsing with parsec?
Imagine the following example
data A = ...
data B = ...
data C = ...
convertA :: A -> C
parseA :: Parser A
parseB :: Parser B
parseC :: Parser C
parseC = do
a <- parseA
if parsed? a
then return $ convertA a
else parseB
Is there a way to implement this kind of logic where I can try to use a parser and if it succeeds, do some conversion on the result and use a different parser otherwise? I know this particular example can be written like this
parseC = (convertA <$> parseA) <|> parseB
but is there a more general way to represent this pattern in monadic notation?
source to share
You can think of it more monadically, but I don't know if I would call it a more general pattern.
Usually, the success or failure of a parser is handled implicitly through the MonadPlus
and interfaces Alternative
. However, if you really want to, you can confirm success / failure and work with it in context Monad
. The function for this is reification optionMaybe
in Text.Parsec.Combinator .
parseC :: Parser C
parseC = do
ma <- optionMaybe parseA
case ma of
Just a -> return $ convertA a
Nothing -> parseB
It is important to note that it optionMaybe
is special. This only succeeds with a result Nothing
if the parser provided to it fails without consuming the input. Of course your example code is broken anyway if it parseA
can consume input on failure, so I assume you are familiar with this problem. This fragmentation is why I hate parsec and will never use it in my code. Sorry for the edit, I just don't think the problem is that every user should be forced to stumble across.
source to share