Fork me on GitHub

So I’m just learning the clj tool coming from leiningen, and when I look at tutorials with a clj command, I find the commands very difficult to read. Here’s an example from Krell:

clj -M -m krell.main -v -co build.edn -c
So I assume that part of this is Krell’s fault because I think everything after krell.main is options to krell, but it took me at least 5 minutes of reading the clj help to figure that out. There’s barely anything about this command that I can intuit without either reading documentation or knowing the tool already.


Also, I find the -M, -X, -T arguments unconventional (I think it’s following the java executable pattern?), and even after reading the clj help, I was confused because it took me a while to figure out that those options are basically subcommands (?). I think this is what Cora and Asko Nomm were talking about earlier.


I don’t think it’s very subjective to state that this:

clj run --function krell.main
is much easier to read than this:
clj -M -m krell.main


I also think the clj help would be easier to understand if it were organized by the different things you can do with it. For example, it would be nice if all the options that only applied to clj -M were grouped with clj -M . It would also be nice to have a bit of elaboration on what e.g. “Run main” means. From the description of main-opt it looks like you could be running a script on standard input or starting a repl or a number of other things.


There are several pieces in play there: -M = run clojure.main and then the options are clojure.main's command-line: a filename with no options -- run that Clojure script, -m invoke a specific namespace's -main function, -e evaluate an expression, -i load a file (to "initialize" what Clojure runs with), -r start a REPL (which is the default if no other options and no filename are given). I'm not sure how best to describe all of that because clojure.main has "always" had all those options, long before the CLI existed so it was documented "elsewhere" (and I'll be honest, even after a decade of Clojure I couldn't say, off the top of my head, how those options can be combined for sure -- I'd have to go digging in the docs or in the source code to be certain). And in the -m case, after the namespace (whose -main will be invoked by clojure.main/-main), you are off in whatever that namespace's behavior is going to be so you are totally at the mercy of how that project, whatever it is, decides to handle command-line arguments.


(I'm much more comfortable with describing the behavior of the newer options -- -X for execute function and -T for tool invocation which I think have much simpler semantics even though they are probably less obvious)

Asko Nōmm22:08:13

I think the main problem, for me at least, is that I have no idea what those one-letter commands stand for. If they would be more descriptive or have a more verbose alternative that would state what it is, like how Sean explains, I'd have a much easier time using the tool.


So perhaps additional supported variants of those options would make it all more intuitive? Like --main for -M, --exec for -X, and --aliases for -A? Something like that?

Asko Nōmm22:08:54

Exactly! I imagine that would be quite backwards compatible also

Asko Nōmm22:08:36

Most CLI-s provide both shorthands and verbose commands, I think, at least from my experience.


I suspect part of the reason here is that clojure.main already has its own (lowercase) options and supports --main (`-m`), --init (`-i`), --eval (`-e`), --repl (`-r`) so it might be confusing to see clojure --main --main krell.main (the first --main is the CLI's option, the second --main is clojure.main's).

Asko Nōmm22:08:44

That way those who already know the tool well can use shorthands, and those who do not yet, can use verbose commands.

Asko Nōmm22:08:13

Ah yeah, that would be confusing indeed


At least right now, the CLI's options are (nearly) all uppercase or at least start with an uppercase letter so they can't be confused with clojure.main options. But, as @alexmiller has noted previously, if clojure script.clj (which is deprecated and suggests using clojure -M script.clj instead) actually stopped being supported, then having some sort of "command" there as a stand-in for the single-letter (uppercase) options might be a possibility but there would still need to be some obvious way to specify aliases for each of main/exec/repl usages -- right now the aliases just follow the option name, e.g., -M:foo:bar. I don't think there's an obvious right answer yet.