We already discussed the reasons you should try Clojure and also configured our development tools and installed Clojure.
As every developer knows since the dawn of computing the first program that we have to write to be successful in any programming language we're learning is print 'Hello World' on console.
For this we gonna start a new project:
$ lein new app hello-world
And open it on our IDE:
$ code hello-world
The project will have the following structure:
. ├── CHANGELOG.md ├── doc │ └── intro.md ├── LICENSE ├── project.clj ├── README.md ├── resources ├── src │ └── hello_world ├── target │ └── default+test └── test └── hello_world
- CHANGELOG: That's a model for CHANGELOG in the project.
- doc: Is a folder dedicated to the documentation of the project, it's initialized with an intro.md for writing the first steps.
- LICENSE: Is a file containing the Eclipse Public License - v 2.0 the same license used in Clojure and default in most Open Source projects that use the language.
- project.clj: That we're gonna declare our dependencies and configuration for the project.
- README: That's a entry document and front page in remote repository for the project.
- resources: Where we can put the files we want to zip in the final jar when we compile the project.
- src: That's where our code will really be.
- target: Where will be the compiled jar's.
- test: That's where the tests will be place.
Now we can run our code using leiningen:
$ lein run
And if we want to build it as a jar (if you're not from Java and not familiar with the concept we'll explain next) for distribute we can use:
$ lein uberjar
If we want to run the generated jar file:
$ java -jar target/uberjar/hello-world-0.1.0-SNAPSHOT-standalone.jar
And we should receive the same message as before.
The JAR (Java Archive) it's a compressed format of a compiled project for Java Virtual Machine (JVM). When the project is compiled to a JAR File all the JVM bytecode generated will be compressed in this file together with metadata and additional resources needed to run the code.
This is analog to what most people think about an auto executable .exe or .dll on Windows. The JAR file is the format that is used to distribute programs written to be executed in JVM or libraries to be used in languages that run on it.
Clojure is 100% written in Java (and Clojure itself). In fact, the whole language runs from a JAR file that can be built from Clojure source code. But the process of compilation and execution of the code it's little different from Java.
When we run a Java program the source code is send to the compiler who will parse, tokenize, structure... the code and then compile it to a .class file that contains the compiled bytecode and that file when run will be sent to JVM who will translate it to the optimized machine code for where it's running.
"Traditional Evaluation (Java)" by Rich Hickey is copyrighted material of clojure.org
By other way, when a Clojure program is evaluated first it passes by a special step that's the reader a program written for receive the source parse it and return all code structured in some data structure, only after it the code gets in compiler. In Clojure the compile only receive data structures and never text, separating the reader from compiling makes it easier to process macros and substitutions. As most times the Clojure program is running in live mode (like REPL) the bytecode is immediately sent to JVM that caches and executes it.
"Clojure Evaluation" by Rich Hickey is copyrighted material of clojure.org
If we use an online Java decompiler tool to analyze the compressed bytecode in the JAR we created when compile our project we'll get that the JAR has a folder structure like that:
hello-world-0.1.0-SNAPSHOT-standalone.jar ├── clojure └── hello-world
Where the clojure folder contains the compiled bytecode of the reader and compiler altogether with the language source compiled.
While the hello-world folder have the original Clojure file we written and also helper classes to run main and these same files compiled in Java, but the compiled result are internal symbols of the Clojure language and not a simple and readable Java program.
So every time we compile our Clojure apps for a JAR to be run in JVM the JAR itself contains also the whole code needed to run Clojure including the the language functions, reader and compiler that can be executed if needed.
Our Clojure journey is still far from the end, now we can understand how the process compilation and execution of Clojure works we can understand how it's different from a Java project and how JVM can execute the JAR since it was not thought to run Clojure code. Hope to see you in part IV.