easy --no-switch complement to --switch
A fairly common idiom in option parsing libraries is for --no-switch to be available as a way to disable an earlier --switch on the command line.
This is useful eg, when you have a shell alias foo = foo --switch, so foo --no-switch can be used to override . Another use case is to help future-proof a program; if it later changes so --switch is enabled by default, then users of both the old and the new version can use --no-switch to get the old behavior.
This is a bit clumsy to do with optparse-applicative, unless I'm missing an easy way to do it. Here's an implementation that makes it easy. I'd be happy if this or something like it were added to your library.
-- | A switch that can be enabled using --foo and disabled using --no-foo.
--
-- The option modifier is applied to only the option that is *not* enabled
-- by default. For example:
--
-- > invertableSwitch "recursive" True (help "do not recurse into directories")
--
-- This example makes --recursive enabled by default, so
-- the help is shown only for --no-recursive.
invertableSwitch
:: String -- ^ long option
-> Bool -- ^ is switch enabled by default?
-> Mod FlagFields Bool -- ^ option modifier
-> Parser Bool
invertableSwitch longopt defv optmod = invertableSwitch' longopt defv
(if defv then mempty else optmod)
(if defv then optmod else mempty)
-- | Allows providing option modifiers for both --foo and --no-foo.
invertableSwitch'
:: String -- ^ long option (eg "foo")
-> Bool -- ^ is switch enabled by default?
-> Mod FlagFields Bool -- ^ option modifier for --foo
-> Mod FlagFields Bool -- ^ option modifier for --no-foo
-> Parser Bool
invertableSwitch' longopt defv enmod dismod = collapse <$> many
( flag' True (enmod <> long longopt)
<|> flag' False (dismod <> long nolongopt)
)
where
nolongopt = "no-" ++ longopt
collapse [] = defv
collapse l = last l
Here's the version of this from stack, which I think is pretty similar: https://github.com/commercialhaskell/stack/blob/0fa72bd825228d65f8e2c2b024d3fff0d7d792e7/src/Options/Applicative/Builder/Extra.hs#L36. Ours also accepts hidden --disable-* and --enable-* versions
In you example of foo = bar --switch, then foo --no-switch; this is monadic parsing as the order then becomes important. I would suggest using something like the example given instead.
Huw Campbell wrote:
In you example of foo = bar --switch, then foo --no-switch; this is monadic parsing as the order then becomes important.
Order can be important in applicative parsing, no monads necessary.
In my implementation sent to this bug, it's handled by getting a list of parses of values for the switch, and using only the last value.
see shy jo
Oh ok, you're using many (optparse provides its own version of many which uses bind instead of apply, it's the one bit using bind in there and I don't want to add any more). I'll have a think about it, it looks like there's a few people who have written something along these lines before.
+1 Would love this feature.
Ping. Has there been any decision whether such a feature would be accepted?