(:gen-class), not only the purpose, but the WHY?

Hi Clojureverse,

I’m on a learning-path and picking up various examples and tutorials to get me started, besides following two excellent books, ‘Getting Clojure’ and ‘Web development with Clojure’. Then I found the following article with the associated GitHub repo, and found it useful for learning and exploring.

I noticed this little statement in the namespace declaration of the core class .

(:gen-class)

Using doc and also reading it on ClojureDoc I roughly get the idea what :gen-class is. But I don’t really understand why this is useful for this particular little script? Do I make a premature assumption when I see this as a design-practice, not really necessary? If so, what is the principle?

It all depends on how you plan to start the Clojure application.

If you intend to launch it from Java directly, you need to declare a :gen-class main, so that a Java Main class is created which Java can use to bootstrap your Clojure code.

If you instead use the Clojure CLI clj to launch it, you don’t need the gen-class.

It doesn’t seem the linked article talks about how they were starting the server app, but since they have a gen-class, my guess is they are doing it through java.

Edit: The github page mentions using lein to run it with the run task. The lein run task also doesn’t need the gen-class. Both clj and lein use clojure.main to bootstrap your Clojure app. So neither needs the gen-class.

1 Like

Thanks for clarifying. I have been running the server from both terminal lein and the IntelliJ REPL with no issues. So, I’ll just leave it as is,… and test it from a Java project as well. I just wanna make sure I don’t pick up any useless habits and idioms.

Regards, Henning

1 Like

The thing is you’re learning and doing practice. In a real production scenario, you would know how your server is deployed and what will kick-start it on the host. Based on that, you’d know if you need to gen-class or not your -main entry point.

I’ll try and detail things a bit more.

First off, Clojure JVM is actually Java.

So in the world of Java and the JVM. If you want to create a program, you need a public Main class which has a public static main method on it. Once you have that, you can call java on your Main class and the JVM will start and run the Java program.

java -cp classes myjavaapp.Main

A Clojure program IS a Java program. Thus to start a Clojure program you can do the same thing you’d do to start a Java program:

java -cp classes myclojureapp.Main

In which case, you need a way to create the Main class from Clojure. That’s what gen-class is for. So by adding a gen-class directive, when you compile your Clojure code, it will create a Java Main class.

Now, there’s another way to start a Clojure program, one that is Clojure specific and only works for Clojure apps, not for Java apps. That’s to use a Clojure based launcher like lein or clj. In those cases, you don’t need to use java, though you still need a JVM installed. When you use those launchers instead of the standard java launcher you don’t need a Java Main class. All you need is a Clojure namespace with a -main function in it. Which also means you do not need to have a gen-class.

clj -m myclojureapp

Or

; Given a project.clj file with :main myclojureapp defined
lein run

Will both similarly launch your Clojure program without needed a Java Main class, and thus you don’t need a gen-class.

If you intend to launch your app both with java and with lein or clj, you will need to add the gen-class. It won’t conflict with lein and clj.

5 Likes

There is another option you missed to run directly from java that doesn’t require AOT or clojure tools.

java -cp classes clojure.main --main ns-name

Where ns-name has a -main function, the name for the main function of your program doesn’t have to be called be -main though, you can do:

java -cp classes clojure.main ns-name

Where ns-name presumably has a function application/call entry point.

3 Likes

One more addition to the excellent answers above: gen-class can also be used to implement a named class that inherits a Java class (proxy can also do this, but it only gave you an anonymous class).

There are some more in-depth discussion if you like to dig further:

1 Like

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.