Search code examples
haskellparser-combinatorsmegaparsec

How to parse a character range into a tuple


I want to parse strings like "0-9" into ('0', '9') but I think my two attempts look a bit clumsy.

numRange :: Parser (Char, Char)
numRange = (,) <$> digitChar <* char '-' <*> digitChar

numRange' :: Parser (Char, Char)
numRange' = liftM2 (,) (digitChar <* char '-') digitChar

I kind of expected that there already is an operator that sequences two parsers and returns both results in a tuple. If there is then I can't find it. I'm also having a hard time figuring out the desired signature in order to search on hoogle.

I tried Applicative f => f a -> f b -> f (a, b) based off the signature of <* but that only gives unrelated results.


Solution

  • The applicative form:

    numRange = (,) <$> digitChar <* char '-' <*> digitChar
    

    is standard. Anyone familiar with monadic parsers will immediately understand what this does.

    The disadvantage of the liftM2 (or equivalently liftA2) form, or of a function with signature:

    pair :: Applicative f => f a -> f b -> f (a, b)
    pair = liftA2 (,)
    

    is that the resulting parser expressions:

    pair (digitChar <* char '-') digitChar
    pair digitChar (char '-' *> digitChar)
    

    obscure the fact that the char '-' syntax is not actually part of either digit parser. As a result, I think this is more likely to be confusing than the admittedly ugly applicative syntax.