Is possible to create a haskell expression, using the methods in optparse-applicative, that parses program options like this?
program [-a [-b]] ...
-a and -b are optionals flags (implemented using switch
), with the constraint that the -b option only is valid if -a is typed before.
Thanks
This is possible, with slight tweaks, two different ways:
-b
if you've got-a
, but you can't insist then that the-a
comes first, since optparse-applicative's<*>
combinator doesn't specify an order.-b
option follows thea
option, but you do this by implementinga
as a command, so you lose the-
in front of it.Applicative is definitely strong enough for this, since there's no need to inspect the values returned by the parsers to determine whether
-b
is allowed, so>>=
is not necessary; If-a
succeeds with any output,-b
is allowed.Examples
I'll use a data type to represent which arguments are present, but in reality these would be more meaningful.
So the options to our program maybe contain an A, which might have a B, and always have a string.
Way 1: standard combinators -
-b
can only come with-a
(any order)I'll use
flag' () (short 'a')
which just insists that-a
is there, but then use*>
instead of<*>
to ignore the return value()
and just return whatever theboption
parser returns, giving options-a [-b]
. I'll then tag that withA :: Maybe B -> A
and finally I'll make the whole thingoptional
, so you have options[-a [-b]]
Notice that since
<*>
allows any order, we can put-a
after-b
(which isn't quite what you asked for, but works OK and makes sense for some applications).Way 2: command subparser -
-b
can only followa
You can use
command
to make asubparser
which is only valid when the command string is present. You can use it to handle arguments like cabal does, so thatcabal install
andcabal update
have completely different options. Sincecommand
takes aParserInfo
argument, any parser you can give toexecParser
can be used, so you can actually nest commands arbitrarily deeply. Sadly, commands can't start with-
, so it'll beprogram [a [-b]] ...
instead ofprogram [-a [-b]] ...
.Which runs like this:
So you have to precede
-b
witha
.