Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

apparently no way to implement --foo[=val] #243

joeyh opened this issue Feb 20, 2017 · 6 comments

apparently no way to implement --foo[=val] #243

joeyh opened this issue Feb 20, 2017 · 6 comments


Copy link

joeyh commented Feb 20, 2017

It's a fairly common pattern for an option --foo to set some default value, and --foo=val to set some more specific value. (With the equals sign being required before the value; a space is ambiguous in this case.)

I cannot find a way to implement this with optparse-applicative, without renaming one of the options from --foo to --bar.

This code compiles, and usage shows "[--foo ARG] | [--foo]", but parsing "--foo" fails.

data Foo = FooStr String | FooBool Bool

parser = (FooStr <$> strOption (long "foo")) <|> (FooBool <$> switch (long "foo"))

I don't really like that as a way of implementing it even if it worked, due to the redundancy.

Could there be a version of option that takes a default value, for when no value is provided?

(#242 touches on this, but is also about a regression)

Copy link

HuwCampbell commented Feb 20, 2017


I agree it's not ideal.

We've been asked about this one a few times and have been cautious in supporting this due to ambiguity concerns amongst other things #235 #67 #109.

But I believe you are correct in that if you enforce that the option must be specified with an = then it will be unambiguously parse-able.

Without writing anything, I believe this will require a new entry in the OptReader sum type, and a new set of builders in order to use it.


Copy link

bbarker commented Sep 14, 2018

Not sure if I'm totally following as I'm new to both Haskell and this library (I guess this is a likely library to catch newbies? not sure ;-)). Anyway, I was trying something similar but for fixed values on the RHS of equals using alternatives as well:

sdlDefaultRendererP = flag' (rendererType(defaultRenderer)) (
  long "renderer=default"
  <> help "Use SDL's default renderer")
hico --renderer=software
Invalid option `--renderer=software'

Did you mean this?

Usage: hico (--renderer=default | --renderer=software)
  Welcome to Hico!

So as we can see, parsing the = seems to be unsupported (if I remove renderer= everything works as expected, except for the fact I'm not sure if I can supply a default among the alternatives, which perhaps is a question I should ask elsewhere).

I think the above is probably what is described in #242 as

Next up is a real bug, in that yes, we shouldn't pop off a flags parse word value if it's specified as a long option.

Anyway, in my particular case, I don't really need =, though I can see the desire in general (I do kind of need a way to specify a default alternative, but feel free to direct me elsewhere for that as it is a bit off topic).

Copy link

HuwCampbell commented Sep 21, 2018

This is unrelated.

So you shouldn't be using flag', nor putting = in your argument. The = sign is handled by optparse, and is equivalent to --renderer default. What you want is something more like

sdlRendererP =
  option renderReader $
     long "renderer" <> help "Select SDL renderer"
     renderReader :: ReadM Renderemajig
     renderReader = auto >>= \case
      "default" -> pure defaultRenderer
      _ -> fail "Nope"

Please open a separate issue if you need additional help. We don't want to bother Joey.

Copy link

Ericson2314 commented Oct 31, 2018

If we could require =, i.e. --foo=bar rather than --foo bar, it would resolve the grammar ambiguity.

Copy link

chshersh commented Jul 4, 2020

@HuwCampbell I think this is a very important feature to have. And implementing optional arguments would increase optparse-applicative a lot. It's a huge deal! While developing tons on CLI tools I started to need optional args more and more.

I'm completely okay with the disambiguation by the = sign. This sounds reasonable to me.

A few examples of how optional args can be used:

  • List issues by filter
    • -m|--milestone — issues from the current milestone
    • --milestone=2 — issues from milestone with ID 2
  • Output to file
    • --toml-output — print output in TOML format in stdout
    • --toml-output=foo.toml — output TOML to the file foo.toml

I think optional args provide much better UX and you need to remember/implement much fewer commands if the feature is implemented.

Copy link

HuwCampbell commented Jul 5, 2020

There's currently an okish way to do this, after the fix for #242.

optArg :: String -> String -> ReadM a -> Parser (Maybe a)
optArg x meta rdr =
  flag' Nothing (long x <> style (<> string ("[=" <> meta <> "]"))) <|>
    option (Just <$> rdr) (long x <> internal)

two :: Parser (Maybe Int, Maybe (Maybe String))
two = (,) <$> optArg "milestone" "INT" (auto :: ReadM Int)
          <*> optional (optArg "output" "FILE" (str :: ReadM String))

main :: IO ()
main = execParser (info (two <**> helper) mempty) >>= print
*Options.Applicative> :main --help
Usage: <interactive> --milestone[=INT] [--output[=FILE]]

Available options:
  -h,--help                Show this help text
*** Exception: ExitSuccess
*Options.Applicative> :main --milestone
*Options.Applicative> :main --milestone=2
(Just 2,Nothing)
*Options.Applicative> :main --output  --milestone=2
(Just 2,Just Nothing)
*Options.Applicative> :main --output=here.toml  --milestone=2
(Just 2,Just (Just "here.toml"))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
None yet
None yet

No branches or pull requests

5 participants