What are the Clojure Tools?

When I start learning a new language I like to begin by understanding the tooling ecosystem. For me, having a handle on 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 Tools. Admitedly, it took me a while to understand their purpose and how to use them and that's where this post comes in.

My goal with this post is to share my knowledge around the Clojure Tools by outlining the problem they solve and how they compare to other tools like lein and boot.

Clojure Tools

If you installed Clojure using the official guide you likely already have the Clojure Tools installed are are using them. Having said this, the Clojure Tools can go by a number of names, and people often refer to the sub-tools within the Clojure Tools suite. Thus, before we go on, let's nail down some naming conventions.

Clojure Tools is an umbrella name and may not even be the official one. It includes tools like clj, clojure, deps.edn and tools.deps.alpha. Thus, you will often here members of the Clojure community referring to each one of these individually. When they are, they are often (context is important here) referring to what I am calling the Clojure Tools. For brevity, I will refer to them all as clj going forward.

Alright, so we have clj available to us, what does it do and how can we use it? Here are some common tasks:

Run a clojure repl

clj

Run a clojure repl program

clj -m your-clojure-program

manage Clojure dependencies

clj -Sdeps '{:deps {bidi {:mvn/version "2.1.6"}}}'

If we were to just look at the above commands, it appears that clj is doing many things. Now, it's okay to think about it like this, but I think it's also important to understand that clj itself is a suite of tools which includes:

Once I understood the above, it became easier to follow along with conversations on forums like Clojurians and understanding the tool was more straightforward. The reason for this is because when the community discusses the clj tool it can seem that clj, deps.edn and tools.deps.alpha are used interchangeably. In truth, they are all separate things that are wrapped by the clj tool.

The next few sections will discuss each of the above tools in more detail and how they all come together.

clj/clojure

clj is the interface to the suite of tools mentioned in the previous section. As mentioned, when you install the Clojure CLI tools you will have access to two commands: clj and clojure. They are just bash scripts and both commands, while separate, actually do the same thing under the hood:

java [java-opt*] -cp classpath clojure.main [init-opt*] [main-opt] [arg*]

But wait, if both clj and clojure do the same thing, why have two commands? Let's dig into this.

Calling the clj command will call the clojure command. The difference between the two is that when you call clj it wraps the clojure command with a tool called rlwrap and then calls clojure.

So why do this? They do this because rlwrap adds readline support to the clojure command. Translation: It makes it nicer 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, the only thing that the clj tool does is:

  • run clojure programs
  • Provides a standard way to interact with clojure programs
  • Improves the "Getting Started" story

But as we mentioned, it will call out to tools.deps.alpha to help resolve dependencies.

tools.deps.alpha

tools.deps.alpha builds a classpath and resolves dependencies. The longer 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

Aside from the above, the best thing you can do to learn more is listen to Alex Miller, the author of tools.deps.alpha, speak about it on Clojure Weekly Podcast.

Finally, as I mentioned tools.deps.alpha knows which dependencies to resolve because it reads in the deps.edn file.

deps.edn

deps.edn allows you to specify project dependencies and configurations.

This is just an edn file where edn is 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 {...}}

This is the file where you define the libraries your project needs, shortcuts and where to find your projects code. Ultimately, it's just a map with some keys. Now, it might seem odd that I think of this as a separate "tool". The reason I do this is because this is just an edn map with well defined k/v pairs.

So what this means is that, in theory, you don't need to use tools.deps.alpha. Instead, you could build your own version of tools.deps.alpha which consumes the deps.edn file and has it's own way of resolving dependencies. I'm not encouraging this, i'm just explaining why I see it as standing on it's own.

Clojure Tools Installer

"Clojure 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. Further, 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. If the same occurs with lein or boot, you will not have any such luck.

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.

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.