What is Clojure and how it is different from ClojureScript?
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 classes
folder!
In 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 project-dir
:
|
|
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.
When we make this distinction clear, it’s easy to understand what ClojureScript is. It is a compiler for the Clojure programming language that turns Clojure code into JavaScript code. (not into Java bytecode as Clojure compiler does).
Just as Clojure allows you to utilize the power of functional programming in tasks and environments where Java is applicable, ClojureScript does the same for environments where JavaScript is applicable.
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 Google Closure Tools
on a previous diagram. Please note, the spelling is Closure; not Clojure! It is a set of tools for JavaScript from Google.
Google Closure includes a lot of useful JavaScript libraries. They are pre-packaged with ClojureScript, and can be used straight away.
Also, there is a Google Closure Compiler that compiles JavaScript into optimized and minified JavaScript. ClojureScript uses Closure compiler when the optimizations setting is turned on.
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 test.cljs:
|
|
The 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:
|
|
As a result of the ClojureScript compilation, you will get a generated JavaScript file, out/main.js
. It can be distributed and used in a JavaScript environment. For example, with node
:
|
|
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.
Conclusion
- Clojure is a functional programming language.
- The Clojure compiler turns Clojure sources into bytecode and runs it on the JVM.
- ClojureScript is another compiler for Clojure language that takes source code and compiles it into JavaScript language.