Note for readers
This article was written for those who are absolutely new to Clojure and ClojureScript and have a problem distinguishing what hides behind these two terms. It is not about nitty-gritty details.
First, we’ll briefly cover what Clojure is and how it works. After that, we’ll see what ClojureScript is and how it’s different.
What is Clojure?
Clojure is a programming language that was created by Rich Hickey. His idea was to make a functional language that has strong concurrency support, and that works on Java Virtual Machine (JVM). Being a JVM-hosted language, Clojure has access to every platform that Java works on, with the ability to utilize a massive amount of preexisting code.
What is JVM?
A basic understanding of JVM is helpful for understanding Clojure, so let’s take a look at what the Java compilation process looks like:
In languages like C++, the compiler takes source code and turns it into a platform-dependent machine code that can be run by OS. Java is different, however. Its compiler takes
*.java source code and compiles it into
*.class files with bytecode. Unlike machine code, bytecode is a platform-independent format. So, Java compilers on different OSes will produce the same bytecode from the same source code.
Then, JVM comes into play. Its class loader brings
.class files to the RAM, verifies for security breaches, and finally, the internal JIT compiler converts bytecode to native machine code that works on the specific target platform where you run it.
So JVM is a set of tools that makes running bytecode on a target platform possible.
What does “Clojure is a JVM-hosted Language” mean?
The two-step compilation shown in the previous section has two perks. First, the Java compiler doesn’t have to know about machine code for different platforms; JVM handles that. Second, you are not restricted to using the Java compiler to get bytecode. You can write your own compiler that produces bytecode, and JVM can still run it.
And this is exactly the approach Clojure takes. Clojure doesn’t have its own tools for generating machine code and running it on the target platform. Instead, it creates bytecode from Clojure sources and leaves the rest of the work for JVM. The diagram of the compilation process looks quite similar to the one for Java:
Clojure compilation example
Let’s run some example code (or just skip this section if you’d like).
If you don’t have Clojure installed, please take a look at the Getting Started guide on the official site for instructions.
Then, create the following folder structure for a simplistic Clojure app:
project-dir (project directory; choose any name you want) ├─ src (in this folder, the compiler will look for sources) │ └─ main │ └─ test.clj (source file with the main function) ├─ classes (the empty folder, where generated classes will be stored) └─ deps.edn (configuration file)
Please make sure you’ve created an empty
test.clj:, we have a helloworld code:
deps.edn: contains configurations for the compiler. Here, we specify the sources folder:
Now, let’s run the compiler from
You will receive an output.
By default, you won’t see the intermediate
*.class files generated. But if you do want to take a look at them, run the
clj command in the
project-dir, and after it shows you a prompt, invoke compilation function:
Now, under the
project-dir/classes/test folder, you will find the generated
*.class files**. If you forgot to create an empty
classes folder at the start, you’ll encounter an error.
What is ClojureScript?
When we refer to the term “Clojure,” we usually mean both the programming language itself, as well as all its tooling. But let’s look at these separately.
There is a “Clojure programming language,” meaning rules and syntax we use to describe how a program should work. Then, there is a “Clojure compiler,” meaning a toolset that takes Clojure language as an input and turns it into Java bytecode.
There is some difference in language support between Clojure and ClojureScript compilers. You can read more details about that on the official site
ClojureScript compilation process
Let’s look at the ClojureScript compilation diagram. It’s different from before, because JVM is missing.
You will still have JVM running, because the ClojureScript compiler is written in Clojure, and as a Clojure app, it’s run on JVM; But it is not essential here, because we can imagine an alternate world where the ClojureScript compiler is written in some other language.
Google Closure Tools
You may be wondering what is
ClojureScript compilation example
Let’s run some ClojureScript code. (As before, feel free to skip this section).
Here is the folder structure for our ClojureScript app:
project-dir (project directory, choose any name you want) ├─ src (in this folder compiler will look for sources) │ └─test │ └─ main.cljs (source file, notice that extension is *.cljs, not *.clj) └─ deps.edn (configuration file)
The main file is
goog.string.format function in our
test.clj is absolutely not necessary for such a primitive app. I added it as an illustration of how easily you can use code from the Google Closure library in ClojureScript.
deps.edn contains a setting that tells that our project needs a ClojureScript package:
Now, lets run the compiler from the project directory:
You should see the output,
Note on compiler arguments
You may be confused by the fact that the command-line arguments for running Clojure and ClojureScript compilers are quite similar, and invoke the same
clj command. Take a look:
This is because the ClojureScript compiler itself is written in Clojure!
So, in the first example, we ran our code,
test.main namespace, as a Clojure app. Similarly, in the second example, we ran
cljs.main namespace, which is a ClojureScript compiler app, and passed additional arguments to it as instructions on how to compile ClojureScript.
- Clojure is a functional programming language.
- The Clojure compiler turns Clojure sources into bytecode and runs it on the JVM.