Hacker News new | past | comments | ask | show | jobs | submit login
Swift: Announcing ArgumentParser (swift.org)
118 points by zdw on Feb 28, 2020 | hide | past | favorite | 26 comments



One of my very, very, very favorite things about Python is argparse. [1]

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

[7] https://github.com/mmahnic/argumentum

[8] https://pypi.org/project/argcomplete/


I can't stand argparse. It's like they tried their hardest to force you to be imperative and prevent composability. For example, instead of being able to define a parser for a subcommand and build a top-lever parser from that, you need to do this:

    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.


Python is indeed an imperative language, along with the others I listed. (Your proposal would indeed be at home in a functional language like LISP.)

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.


> Python is indeed an imperative language, along with the others I listed.

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.


You should check out click : https://click.palletsprojects.com/en/7.x/

It is very powerful, uses special files stdin/stdout when file argument is "-", supports groups, subcommands...


> Very few libraries creates its API so well as to transcend its origins

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.


Docopt is indeed popular. By virtue of having it creating its own language, its API is naturally "transcendent." :)

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.


Argparse is fantastic. I recently had to shift some code from taking input from a csv to taking input from command line and I was dreading how much coding it'd involve. It took me 5 minutes of googling to come across argparse and another minute to find my exact use case written out in 5 lines of code.

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.



Java has picocli https://picocli.info/ which is superb.


If you haven't seen it, I consider the best-in-class argument parsing library to be Optparse-Applicative. https://github.com/pcapriotti/optparse-applicative

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)


Error: The value 'ZZZ' is invalid for '<high-value>'

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.


There's an issue open for this, so they'll probably have it pretty soon: https://github.com/apple/swift-argument-parser/issues/8


Cool! I've recently started writing many of my shell scripts in swift [shameless plug], but nice command line argument processing was something I wasn't looking forward to tackling. Now it looks like I won't have to :)

[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.


I'm somewhat clueless on this front. How usable is Swift outside of Apple OSes? I recall reading here that the language toolchain is broken all the time for other platforms. Is that still the case?


Looks like very nice use case for Property Wrappers demonstration.


I can see it now. Swift will be the first actually code-less language. Just @pile @up @all @necessary @magic @keywords, and your program is complete.

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.


I don’t understand this comment. There is nothing magic there, the @ is just syntax for using property wrappers, which you can write and document yourself, they are a language feature. The argument parser is just a swift library that uses this feature. In particular, @Argument is not a keyword.


Can we please have a 'New Version', 'Old Version' comparison, side by side.

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.


If you write your program well enough (which is not hard), GUI or CLI doesn't matter, you've written a modular tool that can be interfaced with any way you want. And sometimes CLI is a better way to interface (CLIs can be chained together using standard mechanisms much more easily than GUIs).

And why should everything be JSON? It's not the best format ever for all the things (there isn't one).


CLI tools exist for composibility.

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?


Any ETL tool can do tasks like this. It probably has a terrible user interface, but they're enterprise tools and enterprise tools tend to be terrible, regardless of the style or task.

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.


How would I add functionality to add a button that runs that exact command you just typed?

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...


curl, kubectl, jq, ag, grep, git, psql are all tools that are far more useful as a CLI to me. That’s just the few I could come up with top of mind but there are many more.


Personally, I prefer command-line tools over GUIs in many cases. Also, JSON doesn't help me at all if I'm working with other userland tools that expect something else. Piping is very important for CLI tools, so you have to talk the same language as the rest of the userland population.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: