What are the Clojure Tools?
I like to begin learning new languages by understanding the tooling ecosystem. For me, understanding the tools enables me to confidently focus on learning the language itself. Thus, when I approach a new language, e.g. Clojure, I often start by asking questions like this:
- How do I install Clojure?
- How do I run a Clojure program?
- How do I manage Clojure packages (dependencies)?
- How do I configure a Clojure project?
- How do I build Clojure for production?
Now, when I first came to clojure, the answer to the above questions were, "use lein or boot". Then, around the end of 2017, a third option came along: the Clojure CLI Tools.. Admittedly, it took me some time to understand how the Clojure CLI Tools
fit into the bigger picture..
This is where I hope this post will help by providing an overview of what the Clojure CLI Tools are, the problem they solve and how they compare to other tools like lein
and boot
.
Clojure CLI Tools
To begin, what are the Clojure CLI Tools
? They are a CLI tool accessed using the clj
or clojure
command. Furthermore, they are built/maintained by Cognitect, the official maintainers of the Clojure language.
Here are some simple examples of how and what you can do with the Clojure CLI Tools
.
Run a Clojure repl
clj
Run a Clojure program
clj -M -m your-clojure-program
manage Clojure dependencies
clj -Sdeps '{:deps {bidi/bidi {:mvn/version "2.1.6"}}}'
Thus, we can see that clj
is a very useful tool. Now, clj
itself is just a bash script which itself wraps other tools. At the time of this writing, it wraps 3 main tools:
- clojure - another bash script program
- deps.edn - an edn file (just data)
- tools.deps.alpha - a clojure program
The reason why I believe it's important to understand this early is because, no matter what part of the learning journey you're on, it will help.
If you're brand new to Clojure, this is going to help you better understand conversations amongst other Clojurians and help guide questions you may have. For example, if you're ever on Clojurians Slack you will notice that there isn't a Clojure CLI Tools channel, but there is a #tools-deps channel. If you ever have questions about the CLI Tools, that's the place to be.
If you're beyond the early learner phase, understanding the nuance is going to help you level up and gather greater context which can act as a stepping stone as you guide your own learning journey.
In summary, the Clojure CLI Tools
is more of an umbrella term made up of deps.edn
, clj
, clojure
or tools.deps.alpha
.
The next few sections will discuss each of the above tools in more detail and how they all come together.
clojure
When you read the CLI Tools
official getting started you will notice that they use clj
and clojure
and both accept the same arguments. Are they the same thing or different? When do you use one over the other?
First, how are they the same or different? When you use clj
it actually calls clojure
under the hood and clojure
itself calls something like this:
java [java-opt*] -cp classpath clojure.main [init-opt*] [main-opt] [arg*]
Yet, you will see that clj
is more commonly used in development. The reason for this is because clj
wraps the clojure
command with a another tool called rlwrap. What rlwrap
does is add readline support to the clojure
command. In other words, clj
makes it easier to type in the Clojure REPL in the terminal. It's for this reason that you will be encouraged to use clj
during development, where as clojure
is more commonly used in a CI or production setting.
Okay, so now that we know that clj
is a convenience wrapper around clojure
, what does this command do? Well, it's just a bash script and it orchestrates the other tools. Thus, clj
is responsible for:
- running Clojure programs
- Providing a standard way to interact with Clojure programs
- Improves the "Getting Started" story
As I said, it's a convenience wrapper. The meat and potatoes of the clj
tool is tools.deps.alpha
.
tools.deps.alpha
tools.deps.alpha
is responsible for understanding which dependencies your project needs and specifying how to get them. A more detailed way of explaining what it does is:
- reads in dependencies from a
deps.edn
file - resolves the dependencies and their transitive dependencies
- builds a classpath
There isn't too much else going on here and the library itself is small enough that you can read it in an afternoon. If you're interested in learning more I highly recommend listening to the Clojure Weekly Podcast featuring Alex Miller, the author of tools.deps.alpha
, speak about the Clojure CLI Tools
.
Continuing on, in order for tools.deps.alpha
to know which dependencies you need you have to write them out. We do this, and more, in a file called deps.edn
.
deps.edn
deps.edn
allows you to specify project dependencies and configurations. At it's heart, deps.edn
is just an edn file. You can think of it like Clojure's version of json
.
deps.edn
is just a map which accepts specific keywords. Here is an example of some of the common keywords:
{:deps {...}
:paths [...]
:aliases {...}}
With this file we describe the dependencies our project needs, where our project should look to find our source/tests and shortcuts for running our project's code.
Now, given this is just an edn
file it can be odd to think of it as a separate "tool". The reason I believe this is done is because the shape of the edn
map is well defined. Which could be seen as acting like a contract.
What this means is that this file is an extensible tool. In other words, you could write your own tools.deps.alpha
which knows how to consume this file and be compliant with projects which use it.
Clojure CLI Tools Installer
"Clojure CLI Tools Installer" is a fancy way of referring to the brew tap
used to install Clojure on mac and linux machines. As of February 2020, Clojure started maintaining their own brew tap. Thus, if you installed the Clojure CLI Tools
via
brew install clojure
you will likely want to uninstall clojure
and install the following:
brew install clojure/tools/clojure
In all likelihood, you would probably be fine with brew install clojure
. The thing is that while brew install clojure
will still see some love, it won't be as consistent as clojure/tools/clojure
tap.
clj v lein v boot
Let's end this conversation with a quick contextualization of clj
, lein
and boot
.
The first point is that you will choose between one of the three tools (clj
, lein
, or boot
) for your project. You don't use more than one. Just like you wouldn't use both boot
and lein
, you won't use both clj
and boot
or clj
and lein
. Furthermore, none of these tools should conflict with one another.
If you're curious which to choose, I think it's obvious that I would suggest clj
. The reason I like clj
is because the tool is simple and easy to use. You can read through clj
and tools.deps.alpha
in an afternoon and understand what they are doing if you had to. The same (subjectively of course) cannot be said for lein
or boot
.
Secondly, and most importantly, the Clojure community is really leaning into building tools for clj
. For example, where lein
used to have significantly more functionality, the community has built a ton of incredible tools that will cover many of your essential requirements. There is also the fact that deps.edn
is easier to configure because there are less configuration options and less need to understand what lein is doing as you want to perform more advanced configurations.
Finally, when it comes to managing your project configurations and building out maintainable organizational structures (monorepo) it doesn't get easier than clj
.
So yes, clj
for the win.