What are the Clojure Tools?

This post is an overview of the Clojure Tools. When I started my Clojure journey I had questions like:

The short answer to all the above is: use the Clojure Tools .

At a high level, the Clojure Tools currently consist of:

The first is a CLI tool and the second is a Clojure library which provides some helper functions to make it easier to build Clojure artifacts. The rest of this post will dig into each of these tools.

Clojure CLI

The Clojure CLI is a CLI program. Here is what it looks like to use the Clojure CLI and some of the things it can do:

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"}}}'

Like all Clojure programs, the Clojure CLI is built on a few libraries:

The next few sections will review each one in turn.

clj/clojure

As we see above, the Clojure CLI is invoked by calling one of the two shell commands:

When you read through the Official Deps and CLI Guide you will see that you can use either clj or clojure. What's the difference between these two commands? Functionally nothing. They both do the exact same thing. The difference is that clj wraps the clojure command with a tool called rlwrap. rlwrap improves the developer experience by making it easier to type in the terminal while you're running your Clojure REPL.

rlwrap is great if you're a human typing in the terminal, but if your a program running clj you're not gonna have a good time. This is because rlwrap can make it harder to compose the clj command with other tools depending on the tools you use. As a result, it's a common practice to use clojure in production/ci environments . Additionally, not all environments have access to rlwrap so it's another dependency you have to install.

Okay, so they do the same thing. What do they do? clj/clojure has one job: run Clojure programs against a classpath. If you dig into the clj/clojure bash script you see that it ultimatley calls a command like this:

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

Thus, Clojure CLI bash script is a convenience making it easier to run Clojure programs. You don't have to type out a gnarly Java command and make it work on different environments (windows, linux, mac etc). However, it orchestrates the building of the classpath by calling out to tools.deps.alpha.

tools.deps.alpha

tools.deps.alpha is a Clojure libary responsible for managing your dependencies. It does the following things:

What's interesting about this program is that it's just a Clojure library. This means that you can use it outside of the Clojure CLI.

The other thing that makes tools.deps.alpha great is that it's a small and focused library. Why this is great is that if something goes wrong it's easy to read and learn the library in a short period of time.

Now, if you're of the opinion that you shouldn't have to know implementation details of the tools you use, well, welcome to professional software development. It doesn't matter what language or tool you use, you will run across outdated docs, bugs or undefined behaviour. You could choose to wait for someone to save you, or solve the problem yourself. This is where small, well designed and focused libraries win out over battery included solutions. To learn more about the history, development and goals of the tool from the Clojure team I recommend listening to this episode of Clojure Weekly Podcast which features Alex Miller, the author of tools.deps.alpha.

Tangents aside, in order for the classpath to be built we need to tell tools.deps.alpha what we need on the classpath. This is the job of deps.edn.

deps.edn

The deps.edn file is a Clojure map with a specific structure. Thus, when you run clj/clojure one of the first things it does is find a deps.edn file and reads it in.

deps.edn is where you configure your project and specify project dependencies. At it's heart, deps.edn is just an edn file. You can think of it like Clojure's version of package.json.

Here is an example of what a deps.edn file looks like:

{:deps    {...}
 :paths   [...]
 :aliases {...}}

As you can see, we use the keywords :deps, :paths and :aliases and more to start to describe your project and the dependencies it requires.

Tools.Build

This is the newest Clojure Tool. It's been in the works for a while and might be the simplest to understand conceptually: It's a Clojure library with functions that do things like build a jar, uberjar etc.

One distinction that's important to note is that tools.build is not the same as the Clojure CLI tool's -T switch. I am calling this out now because when tools.build was released the Clojure CLI was also enhanced to provide the -T switch. As one can imagine, this could be seen as confusing because of the similarity of their names.

The best way that I can currently explain the -T switch is by saying that it's meant to be another level of convenience provided by the Clojure CLI.

Regarding usage, it helps to first breakdown the main types of Clojure programs one might build into 3 sub categories:

You would use -T for Clojure programs that you want to run as a "tool". For example, deps-new is a Clojure library which creates new Clojure projects based on a template you provide. This is a great example of a Clojure project which is built to be a "tool".

I don't want to go into more detail about -T now because that means we would have to dive into other Clojure CLI switches like -X and -M. That's for another post. On to the Installer!

Installer

The "Clojure CLI 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 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 as it will recieve updates. However, while brew install clojure will still see some love, it won't be as active as the clojure/tools/clojure tap.

clj v lein v boot

This section will provide a quick comparison of clj, lein and boot.

Firstly, all of the above tools are more or less addressing the same problems in their own way. Your job is to choose the one you like best.

If you're curious which to choose, my answer is the Clojure CLI. The reason I like the Clojure CLI is because the tool is simple. 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. This is not just implementation, but also usage. Yes, lein seems easier to start, but the moment you break away from the beginner examples you are left deeps in the woods without a compass.

Secondly, the Clojure Tools promote libraries over frameworks. This is important when working with a language like Clojure because it really does reward you for breaking down your thinking.

Finally, the Clojure community is really leaning into building tools for Clojure CLI. 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.

So yes, Clojure Tools for the win.