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?

+3


source to share


1 answer


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.

+8


source







All Articles