Problem
It is useful to add custom behavior for program entry points. Examples of such behavior:
- calling (shutdown-agents)
to ensure graceful shutdown;
- calling (Platform/exit)
for JavaFX applications to ensure graceful shutdown;
- rebinding System/out
and System/err
in test runners to collect all output.
The -main
function for -M
-style entry points is a natural place to put these behaviors.
One property of these behaviors is that they might be very useful for entry points, but they affect JVM in such a way that these programs might be impossible to integrate into longer-running programs.
The situation with -X
-invokable functions is where this property is problematic because these are supposed to be invokable both as an entry point and as a part of a longer-running program called by the user of the dependency, but there is no way to tell those apart from inside the function.
Considered solutions
- Do nothing. Library authors can add optional arguments to their
-X
-invokable functions that disable special entry-point behavior, although this might be annoying to users if their programs break in unexpected ways when invoked from user code. Library authors can add optional args to enable special entry-point behavior instead of disabling it, but it makes the library more inconvenient to use at the command line. Library authors can create separate -X
-invokable functions for use at the command line and at the user code, but it decreases the usefulness of the whole idea of having the same API in code and at the CLI with -X
.
-X
-style invocations can be given an extra argument by clj-exec that hints that it is invoked as an entry point, e.g. clj -X clojure.core/prn :a 1
will print {:entry-point true :a 1}
. I think this is behavior is unexpected and might be confusing or even annoying.
-X
-invokable vars can define metadata that will be added to the arg when invoked as an entry point, e.g.:
(defn ^{:default-clj-exec-argmap {:rebind-system-err true}} do-stuff [argmap] ...)
Then, running clj -X my.ns/do-stuff :other :args
will use {:rebind-system-err true}
as a default argmap that will then be merged with other args.