Swift: Announcing ArgumentParser
swift.org
swift.org
Very few libraries creates its API so well as to transcend its origins (JodaTime comes to mind).
* Node.js [2]
* Java/JVM [3]
* Go [4]
* Lua [5]
* C++ [6] [7]
It appears there is no Swift port.
That is unfortunate, because besides the basics (optionals, positions, defaults, short and log options, auto generated docs, subcommands, descriptions, repeating options), it lets you do argument groups, mutually exclusive options, customizable help behavior, variable customization, custom parsers/validators, options with multiple arguments. There's even Python support for shell autocomplete library. [7].
(One potential limitation is that it is naturally dynamic, and not typesafe like Swift's new ArgumentParser. IMO that is of relatively small practical importance compared to its other features.)
It's both simple enough to get going, but sophisticated enough that I've never wanted for anything. It's the first thing I look for when writing a CLI.
[1] https://docs.python.org/3/library/argparse.html
[2] http://nodeca.github.io/argparse/
[3] https://argparse4j.github.io/
[4] https://github.com/akamensky/argparse
[5] https://github.com/mpeterv/argparse
[6] https://github.com/jbms/argparse
At least in the argument parsing space, docopt[0] did as well, with official implementations in ~22 languages. It might not always be the best native approach, but the one I will reach for when I have to do argument parsing in a new language.
I've used it in Ruby, for which IMO it is the best available choice.
But there is only so much you can express in docopt, and I quickly begin missing the flexibility of a "real" API and argparse.
The kicker is that once you define it and let it capture inputs, it outputs absolutely standard professional level error messages for bad parameters. I did not have to write any exception handling code at all. I could not believe that what I thought would take me several days was done in less than an hour.
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
a = subparsers.add_parser('a')
a.add_argument('bar')
A better API would look more like this: Parser(Subparsers({
'a': Parser(Arg('bar')),
}))
On top of that it has a number of bugs in edge cases they refuse to document or fix. For example, you can't have a subcommand that always collects all the remaining arguments as-is. That's a pretty basic use-case when you want to pass those arguments to another program.But I reject the idea that imperative is bad. You can compose imperative programming fine.
def my_parser(parser):
parser.add_argument('bar')
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
my_parser(subparsers.add_parser('a'))
my_parser(subparsers.add_parser('a2'))
> you can't have a subcommand that always collects all the remaining arguments as-is. parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
a = subparsers.add_parser('a')
a.add_argument('args', nargs='*')
That works just fine. ./example a arg1 arg2 arg3
./example a -- arg1 --look-ma-hyphens-arg2 arg3
If you're complaining about the double hyphen syntax....that's a universal convention for UNIX-ish CLI positional arguments. Otherwise things get really murky. ./example a --help
Did I want help on the `a` subcommand, or did I want to pass `--help` to the `a` subcommand? The `--` syntax lets you distinguish.JavaScript has functional origins, and in practice quite a bit of JavaScript is written in a functional-lite style. As a JS developer, I find it quite infuriating that Python doesn't support these patterns, as they make code a lot more readable, and it seems to be a matter of principle rather than a technical limitation.
It is very powerful, uses special files stdin/stdout when file argument is "-", supports groups, subcommands...
System.CommandLine https://github.com/dotnet/command-line-api
CommandLineParser https://github.com/commandlineparser/commandline
NDesk.Options https://github.com/Latency/NDesk.Options
Mono.Options https://github.com/mono/mono/tree/master/mcs/class/Mono.Opti...
It provides an applicative interface, which it turns out is very precisely impedance-matched to the nature of command line arguments.
It also integrates nicely with e.g. https://hackage.haskell.org/package/optparse-generic to automatically derive command line parsers from data structures.
An example from a recent process of mine: https://github.com/wyager/zfs-backup/blob/master/src/Lib.hs . You can see what the generated help text looks like in the README.
The argument parser is automatically derived from the data structure describing the commands available in my program, using optparse-generic. Some extra type annotations on the command data structure provide the detailed document text.
Of course, you don't have to use the fancy auto-generation stuff if you don't want to. Writing optparse-applicative parsers by hand is very pleasant as well. Here is the argument parser code for my website's server: http://paste.best/p/22_rCSCen8M=
@Argument(help: "The highest value to pick.")
var highValue: Int
func validate() throws {
guard highValue >= 1 else {
throw ValidationError("'<high-value>' must be at least 1.")
}
}
Not saying there's anything wrong with this. But I wrote a generic command line parsing library for C# some years back, and simple validators like min / max values were handled with attributes. The elegance was that limits were defined in exactly one place in the code, and error messages like that manually constructed above were synthesized automatically. (Of course you could override with your own validation function for bespoke cases)That is an awful default error for validating the type. The program knows it failed to parse as an Int but doesn't tell you anything about that.
Error: The value 'ZZZ' is invalid for '<high-value>' of type Int
Also, the help should automatically tell you the expected types as well.
ARGUMENTS: <high-value>: Int The highest value to pick.
[shameless plug]: https://github.com/cobbal/swsh
Description:
A shell-scripting library for Swift, inspired by scsh.
swsh makes writing shell scripts more fun by exchanging bash (or similar) for a better thought-out language like Swift. In the process, a small amount of conciseness is traded for better quoting, error handling, and access to libraries.
Seriously, instead of doing things actually worth mentioning as programming language development, they kill time with collecting cute magic tricks? Swift could have been interesting but featurism and mission creep make it a bag of complicated magic even worse than C++. At least C++ templates let me build my own magic. I have a strong suspicion that I will never use Swift voluntarily.
If this is an improvement, why are you telling me instead of showing me?
Personally, the problem is command line tools, not argument parsing. Why would anyone create a command line tool instead of a GUI that only lets you enter the correct set of arguments that go together, grouped into a coherent UI?
The end result should be a json. We all know how to parse json in Swift and every other language in existence.
And why should everything be JSON? It's not the best format ever for all the things (there isn't one).
How many lines non-ignored lines are in my git worktree?
git ls-files -coz --exclude-standard | xargs -0 cat 2>/dev/null -- | wc -l --
How would you ever create a GUI with that much flexibility?You're right that most GUIs don't tend to be composable -- but that's a function of how they're written, not an inherent limitation of GUIs. There's nothing fundamental about the VT100-style interface that makes it optimal for composition. And GUIs potentially offer some huge benefits, like not having to guess at what's flowing through the pipelines, or having a less painful way to deal with quoting/escaping, or offering an alternative to cryptic flags for configuring operations.
Screen editors used to get the same scorn. You probably haven't heard the term "screen editor" in a few decades because every editor today is a screen editor. Once they acquired most of the flexibility of line editors, it turned out that nobody actually preferred line editors.
People want composability, and you can do that in a screen editor better than a line editor -- and a GUI better than a terminal. You just have to have designers who care about it.
By adding an 'add button' with a text field where you type your bash script.
All I have to do to make a GUI superior to your terminal is to add some GUI to an existing terminal that people find useful...