Perform checks that include multiple options

61 views Asked by At

I have

type Month = Int

parseMonths :: OP.Parser (Month, Month)
parseMonths =
    liftA2 (,)
        (OP.option
            (OP.eitherReader $
             parseNumber "month" (\n -> 1<=n && n<=12) "month")
            (OP.metavar "MONTH" <>
             OP.long "from-month" <>
             OP.value 1))
        (OP.option
            (OP.eitherReader $
             parseNumber "month" (\n -> 1<=n && n<=12) "month")
            (OP.metavar "MONTH" <>
             OP.long "to-month" <>
             OP.value 12))

I want to add a check, that the first month is not after the second month. Obviously I cannot do that in OP.ReadM. Can I perform the check in OP.Parser? Or do I have to perform the check after parsing with parserFailure like here: Optparse-applicative: consecutive parsing (ReadM) ?

1

There are 1 answers

1
Isaac van Bakel On

Can I perform the check in OP.Parser?

No.

Parser is only an Applicative and not a Monad (hence the package name, optparse-applicative). This is a typical feature of parser functors - because the Applicative instance collects as many errors as possible, there is no possible lawful Monad instance.

Without being a Monad, it's impossible to write Parsers that depend on the result of other Parsers - since that's the defining feature of monads.


However, optparse-applicative does offer an escape hatch, if you really need one: use ParserM.

ParserM is basically an alternative to Parser which is a Monad - at the cost of dropping error messages some of the time. You can write a monadic parser using ParserM, and then use runParserM to turn it back into a regular Parser.