简体   繁体   中英

How can I do conditional monadic parsing using 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 such logic where I can try to use a parser, and if it is successful do some transformation on the result, and otherwise use another parser? I know this particular example could be written such as the following

parseC = (convertA <$> parseA) <|> parseB

but is there a more general way to represent this pattern in the monadic notation?

You can represent it more monadically, but I don't know if I'd call it a more general pattern.

Normally, success or failure of a parser is handled implicitly through the MonadPlus and Alternative interfaces. However, you can reify success/failure and operate on it in the Monad context if you really want to. The function to do that reification is optionMaybe in Text.Parsec.Combinator .

parseC :: Parser C
parseC = do
    ma <- optionMaybe parseA
    case ma of
        Just a -> return $ convertA a
        Nothing -> parseB

One important note here is that optionMaybe is.. special. It only succeeds with a Nothing result in the case when the parser provided to it fails without consuming input . Of course your example code is broken anyway if parseA can consume input while failing, so I assume you're familiar with that issue. This brokenness is why I hate parsec and will never use it in my own code, by the way. Sorry for the editorializing, I just don't think that issue is something every user should be forced to stumble over.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM