This guide will take you from a Mac with Homebrew without a Clojure development environment, to a Mac with homebrew and with a Clojure development environment.
First, install Clojure with Homebrew:
brew install clojure/tools/clojure
This will install two executables on your system: clj
and clojure
.
clj
is for you to run in a terminal, and clojure
is for other applications.
Actually, you can completely forget that you ever heard there is a clojure
command, as you might never need it.
If you run clj
in a terminal now, something will crash.
Just try it if you want to see the error message.
You get an error because two system dependencies are missing, a Java Virtual Machine (JVM) in short, and rlwrap
.
We need a JVM because Clojure runs on the JVM, and has access to the JVM ecoystem. You can choose any Java version and distribution you want. In this guide, we will use the temurin java distribution because it's a good choice, and install the latest Java version because it mostly just works, and then we don't have to care about java. I (Teodor) have never experienced any breaking change in my code resulting from updating a Java version.
Finally, let's install that "Temurin JVM".
brew install --cask temurin
You will see lots of text, then a very important message. The message asks you to "pick java version" for your mac. Why? Because Apple made a mechanism in Darwin to the user to choose which Darwin version to use. This is a good thing! It means you can select Java version once, then it works.
Read the message carefully, then select temurin as your java version of choice.
Now, install rlwrap
:
brew install rlwrap
You don't have to understand rlwrap
to do any of this.
But it will make your editing experience nicer in certain ways, and is a recommended thing to install when setting up a Clojure REPL.
Specifically, it will make clj
support GNU readline-like actions in terminal based Clojure repls.
Without rlwrap, arrow up, arrow down and ctrl+R will behave weirdly.
With rlwrap, you'll get something nicer.
We do not start and restart the JVM to run new code. Instead, we interactively load our code changes, and observe new behavior.
To understand what interactive programming means, you may either read Erik Sandewall's 1978 paper Programming in an Interactive Environment: the "LISP" Experience and Jack Rusher's 2022 talk Stop Writing Dead Programs.
If you do not intend to write your program in an interactive manner, I recommend staying as far away from Clojure and other Lisp dialects as you can. There are plenty of good programming languages that expect the user to work in a compile-restart-test-cycle. Try Python, Java, C, C#, Haskell, Elm, Ocaml, Rust, Go, Javascript or typescript, to name some.
Phew.
If you do want a database for local development, set up Docker Compose and run
docker compose up
, then continue. Keep the terminal where you are running the database open.
Calva is a great tool for Interactive Clojure Programming. Install it from Visual studio code.
There are other good options, but this guide will not cover those options.
In VSCode,
- Open the command palette,
- Run Calva: Start a Project REPL and Connect
- For Project Type, select deps.edn
(because Mikrobloggeriet uses deps.edn, look for a file called
deps.edn
) - For alias, select :dev
You now have a Clojure REPL! From this Clojure REPL, we can run any code. Let's start the server.
In the REPL window, run
user> (start!)
Don't write the user> part, just write (start!)
.
start!
is a function.
By wrapping it in parentheses like (start!)
, we run it with zero arguments.
start!
is defined in src/user.clj
, and is a function we've written ourselves.
user>
means that you are in the user
namespace.
user/start!
is only for local development, the server uses a different entrypoint in production.
You should now see a web browser pop up. Congratulations! It works!
Mikrobloggeriet has unit tests. To run run all unit tests once in a terminal, running
bin/kaocha
in a terminal.
To run all the tests automatically each time you save a file, running
bin/kaocha --watch
in a terminal.
Keeping a terminal open that runs all the tests all the time is a really nice way to know whether the changes you are making are working. Try it out!