Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does the - (minus) symbol in front of function name within Clojure mean?

Tags:

clojure

I can't get my head around the following. When defining the main function in Clojure (based on code generated by Leinigen), there's an - symbol in front of the function name main.

I went to the original documentation on clojure.com and found defn and defn- among other things, see https://clojuredocs.org/search?q=defn. I also searched on Google and found a source that said that the - in front of main indicated that the function was static (http://ben.vandgrift.com/2013/03/13/clojure-hello-world.html).

Does the - truely mean that the function is static? I couldn't find any other sources that confirmed that. Also I can use both (main) and (-main) when calling the main method without any problem.

Given the following code...

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (println "Hello, World!"))

(defn main
  "I don't do a whole lot ... yet."
  [& args]
  (println "Hello, World!"))

I get the following output...

(main)
Hello, World!
=> nil
(-main)
Hello, World!
=> nil
Loading src/clojure_example/core.clj... done
(main)
Hello, World!
=> nil
(-main)
Hello, World!
=> nil

I noticed no difference. Output is the same for both functions. Any help is appreciated!

like image 882
M. Groenhout Avatar asked Oct 25 '19 13:10

M. Groenhout


People also ask

What is a symbol in Clojure?

There may be some confusion here from the different usages of the term "symbol" in Common Lisp and in Clojure. In Common Lisp, a "symbol" is a location in memory, a place where data can be stored. The "value" of a symbol is the data stored at that location in memory. In Clojure, a "symbol" is just a name.

What is a Clojure function?

Clojure is a functional language. Functions are first-class and can be passed-to or returned-from other functions. Most Clojure code consists primarily of pure functions (no side effects), so invoking with the same inputs yields the same output.


1 Answers

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

like image 60
Alan Thompson Avatar answered Sep 20 '22 18:09

Alan Thompson