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:

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.