What are the Clojure Tools?
When I start learning a new language I like to begin by understanding the tooling ecosystem. For me, understanding the tools enables me to confidently focus on learning the language itself. For example, when I came to Clojure my questions went something 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.. Admitedly, it took me a bit to understand how they fit into the bigger picture which is the reason i'm writing this post .
My goal is to provide an overview of what the Clojure CLI Tools are by outlining the problem they solve and how they compare to other tools like lein
and boot
.
Clojure CLI Tools
If you installed Clojure using the official guide you likely already have the Clojure CLI Tools
installed and are using them.
To start, when I say Clojure CLI Tools
I use it as an umbrella term to describe a suite of tools which we access using the clj
or clojure
terminal command. Here are some examples of how to use the Clojure CLI Tools
.
Run a clojure repl
clj
Run a clojure repl program
clj -M -m your-clojure-program
manage Clojure dependencies
clj -Sdeps '{:deps {bidi/bidi {:mvn/version "2.1.6"}}}'
If we were to just look at the above commands, it looks like just one tool: clj
. While it's fine to think of it like this, it's important to understand that clj
is a command which calls other tools. The tools it calls under the hood, at the time of this writing, include:
- clojure - a separate command
- deps.edn - an edn file (just data)
- tools.deps.alpha - a clojure program
The reason we want to understand the tools behind the command is because it's going to make it easier to participate in Clojure community conversations. The reason for this is because when discussing the Clojure CLI Tools
community members will often reference deps.edn
, clj
, clojure
and tools.deps.alpha
in that context. Sometimes, they can be used interchangeably and as shorthands.
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 guide 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.
So to recap, clj
and by extension the clojure
command are responsible for:
- running Clojure programs
- Providing a standard way to interact with Clojure programs
- Improves the "Getting Started" story
The next topics 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 code and 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. Now, there isn't really a need to do this, but it's an example to illustrate why it can be seen as a tool.
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 command line 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 the other.
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.