I’m very pleased to announce the next major version of optparse-applicative. I was meaning to release this last month, but I had a baby last month instead, which has taken up quite a bit of my time. Still, it’s been a pleasure to put this release together, and I hope people enjoy the results. In this post, I’ll mention a few of the new features and fixes.
If you’re not yet using the completions scripts for optparse it’s well worth giving it a try now. I’ve fixed quite a lot of bugs, and added a few things which almost completely negate the need for writing custom shell scripts for completions for optparse programs at all.
The first few bugs related to how bash compgen actions were called, and these bugs got pretty ugly at times. For example, if one tried to complete a term which was partially quoted, such as
> example 'Documents/<tab>
then a particularly awful error message regarding syntax errors would be emitted to stderr by the called bash process as it tried to find the close to the quotion.
The solution was to dequote any quoted or partially quoted string,
then hard quote it again before passing it to the
in bash. This also had the added benefit of fixing one of my personal
annoyances for our completion system – premature expansion of
a path completion: where
This is now fixed, and the
~ does not get rewritten on the command
line by a bash completion.
Beyond bash though, we now support much richer completions for zsh and fish shells. What’s particularly nice is that these completions also now can include the help descriptions for the items being completed.
Here is an example screenshot when using fish shell using only the uncustomised optparse generated completion script
A similar level of detail is available with zsh completions as well.
One of my focuses for this release was to ensure that optparse gives a great error message for every type of failure we emit (this has actually been a focus of mine for some time); and there was one which was oddly handled by default.
option has had a builder,
noArgError which one can use
to specify the error message shown to a user if they do not provide
an argument for the respective option. We have used this for the
helper parser for instance such that it always shows the
help text. The default for this was a conspicuously unhelpful though,
ErrorMsg "". Now instead of not receiving an error message,
one should see
The option `--bash-completion-script` expects an argument.
for example. This did require a change to the internal types to store the name of the option, but I managed to keep the builder api the same, so one can still use the old builder to change the error message if they desire.
Combined with other error message improvements mentioned in my previous
post I don’t
think one will ever receive an unhelpful or absent error message from an
optparse-applicative parser going forwards, which I think is a great
When an option, flag, or subcommand can’t be parsed, or no match for it could be found, we now can emit some suggestions of terms which the user may have meant. This is based off the edit distance between the entered argument and the possible items the parser knows about. Details regarding the implementation, are available in my last post
The end result looks a lot like git’s command completion, and is quite nice I think
Invalid argument `installl'
Did you mean this?
Invalid option `-x'
Did you mean one of these?
Optparse has had a no intersperse option builder for a good while
(since 0.7), but its description and behaviour didn’t actually
match. When a parser was called with
noInterperse applied, it
would behave the same as if the user had typed
-- at the start
of the command: that is, no options would be parsed. In other parsing
libraries with no intersperse options on (and old unix command line
guidelines), one has to place all options before any arguments.
Once a single positional argument is parsed, all remaining options
are treated as positional as well. We now behave in the same way.
Fixing this issue inspired me to also alleviate a pain point which
my colleagues and I had been having regarding mixing arguments and
wrapping commands. At Ambiata, we created a build tool called
mafia which acts as an opinionated wrapper around
nix like package cache, safe sandboxing, and submodule
handling amongst other things.
When using it though, to pass an option to
cabal underneath, the
user would need to first use
-- to tell optparse to treat all
remaining options as arguments, and the argument list would be sent
cabal. Now, with the
forwardOptions builder attached when
parserInfo structures, any unrecognised options to
mafia can be passed through as is. I believe this will actually
help in quite a few places where people are using mixed environments
(such as a C
command line parser).
Text and Bytestring are pervasive in the Haskell ecosystem, so it
was annoying that
optparse-applicative didn’t support them trivially
out of the box.
The builders we have provided for some time are
str :: ReadM String
auto :: Read a => ReadM a
which I have found quite funny, in that
auto is good for simple types
apart from string like ones (which require quoting when used with
str requires an
fmap pack to make it work with
Text. From 0.14
onwards these will be
str :: IsString a => ReadM a
auto :: Read a => ReadM a
which means that the
str reader will be able to deal with
ByteString, as well as their lazy equivalents without
requiring the user to jump through any hoops. Other builders and
helpers such as
strArgument have also been updated
to be polymorphic over
IsString in the same manner. This is a mildly
breaking change, but I think it’s worthwhile.
If one has been using
str for the purposes of type inference (as
we were in the
optparse-applicative test suite) then type signatures
may now be required, or the use of
readerAsk, which is still fixed
to a single type. As there’s not a lot of purpose in requesting a
value and not using it, I don’t think many users will see any update
issues from this change at all.
The final big thing I have done before release is to remove the
module reëxports from the
Options.Applicative module and explicitly
export a public API.
This should make things more robust going forwards, and let people know exactly what is a supported and useful part of the API, instead of having them have to infer if something is actually internal to the library.
In doing this I have actually removed a few function exports which
were internal or deprecated, and added the
ParserHelp type (which
is useful) to the main module exports.
Again, this is a minorly breaking change, it is however quite unlikely that it will cause issues for most users, and indeed, it should make it easier to maintain a rock solid API going forwards.
Thanks to Paolo Capriotti for trusting me to maintain his library, and to those who have submitted patches and bug reports and given advice. Herbert Valerio Riedel was especially helpful with keeping things compatible across past and (with any luck) future versions of GHC.