First, an aside.
Regarding the macros defn vs defn-, the 2nd form is just a shorthand for "private" functions. The long form looks like:
(defn ^:private foo [args] ...)
However, this is just a hint to the user that one shouldn't use these functions. It is easy for testing, etc to work around this weak "private" restriction. Due to the hassle I never use so-called "private" functions (I do sometimes use metadata ^:no-doc and names like foo-impl to indicate a fn is not a part of the public-facing API and should be ignored by library users).
The "main" function in a Clojure Program
In Java, a program is always started by calling the "main" function in a selected class
class Foo
public static void main( String[] args ) {
...
}
}
and then
> javac Foo.java ; compile class Foo
> java Foo ; run at entrypoint Foo.main()
Clojure chooses to name the initial function -main. The hyphen in the function name -main is not really special, except it makes the name unusual so it is less likely to conflict with any other function in your codebase. You can see this in the definition of the function clojure.main/main-opt.
You can see part of the origin of the hypen convention in the docs for gen-class (scroll down to see the part about :prefix). Note that using the hyphen is changeable if using gen-class for java interop.
Using the Clojure Deps & CLI tools, the name -main is assumed as the starting point of the program.
If you are using Leiningen, it is more flexible and allows one to override the -main entrypoint of a program.
In Leiningen projects, an entry like the following indicates where to start when you type lein run:
; assumes a `-main` function exists in the namespace `demo.core`
:main ^:skip-aot demo.core
so in a program like this:
(ns demo.core )
(defn foo [& args]
(newline)
(println "*** Running in foo program ***")
(newline))
(defn -main [& args]
(newline)
(println "*** Running in main program ***")
(newline))
we get the normal behavior:
~/expr/demo > lein run
*** Running in main program ***
However, we could invoke the program another way:
> lein run -m demo.core/foo
*** Running in foo program ***
to make the foo function the "entry point". We could also change the :main setting like this:
:main ^:skip-aot demo.core/foo
and get behavior:
~/expr/demo > lein run
*** Running in foo program ***
So, having the initial function of a Clojure program named -main is the default, and is required for most tools. You can override the default if using Leiningen, although this is probably only useful in testing & development.
Please keep in mind that each namespace can have its own -main function, so you can easily change the initial function simply by changing the initial namespace that is invoked.
And finally, the hyphen in -main us unrelated to the hyphen used for pseudo-private functions defined via defn-.