Clojure Impressions Round Three

The Clojure IDEs used in this exercise: Emacs + Cider and Intellij IDEA + Cursive.

Introduction

This Clojure Simple Server is a re-implementation of my original Clojure Simple Server I did some three years ago. I created the first version of that Clojure exercise server to learn to use Clojure. I created this new Clojure Simple Server version mainly to learn new Clojure libraries and new ways to work more efficiently with the Clojure REPL. If you are interested about my Clojure learning history you can read my earlier Clojure related blog posts:

Using a REPL Scratch File

I watched Chicago Clojure — 2017–06–21 — Stuart Halloway on Repl Driven Development presentation and I realized that previously I had done REPL driven development completely wrong: I had written all REPL experimentation in the REPL editor. Instead you should use some scratch file and just send the S-expressions to the REPL for compilation and evaluation. This way you can have all your experimentation saved in a scratch file. In this exercise I actually created three kinds of Clojure files:

  1. test: the test source code. This code is for testing purposes and is not shipped into production but will be added also into the Git repository, of course (e.g. to be run in the CI server).
  2. dev-src: the development source code. This code is just for development purposes and you don’t necessarily add this code into the git repository (and you don’t package this code as part of the deployment unit, of course). I have added this code also in the Git repository for educational purposes. In this directory I have two files:
  • myscratch.clj: the REPL scratch file I talked earlier. This file is pure unstructured mind flow. The idea of this file is what Stuart Halloway is talking about in the above mentioned presentation: do not write code in the REPL editor but in a scratch file. So, in this file I experimented various things when developing the application — take a look to have an idea what I was thinking about when developing the application.
(do
(in-ns 'simpleserver.webserver.server)
(start-web-server (get-in simpleserver.util.config/config [:server :port]))
(let [
_ (require 'mydev)
ret (mydev/do-get "/info" {})]
(prn (str "/info returned: " ret)))
(stop-web-server)
(in-ns 'user))

You Can Do It Without Application State Management Libraries

I realized that for a simple application like this exercise you don’t actually need some Clojure application state management library (like Component, Mount or Integrant). And personally I also realized that it is better to learn to compile your Clojure code in those three categories (individual S-expression, def and defn top forms and the whole namespace) when you change something than just blindly “refresh all namespaces and rely some application state management library dependency graph magic to do it for you so that you don’t need fully to understand what actually happened”. Well, this is my personal feeling and there might be reasons for more complex applications to use some real application state management library — let’s see when I have a chance to do something more complex using Clojure hopefully in the near future. So, my recommendation is first to try to manage without any application state management library and try to understand how to compile and load certain Clojure constructs manually.

Rich Comments

This is a trick I learned from one of Stuart Halloway’s excellent videos. Add at the end of your Clojure (production) file a “rich comment” which demonstrates how to use the entities defined in that namespace. Example from: domain_single_node.clj:

(comment
(-get-raw-products 1)
)

Debugger

You don’t need a debugger with Clojure. :-) There is a debugger in IntelliJ IDEA + Cursive (and in Emacs + Cider), but I only once tried that it works. If you want to learn good debugging practices in Clojure you need to learn to use the REPL efficiently. Good resources are the above mentioned REPL Driven Development course and Chicago Clojure — 2017–06–21 — Stuart Halloway on Repl Driven Development video. E.g. in that video Stuart shows how to implement a simple breakpoint using Cursive — i.e. create your own debugger!

Linting

I used clj-kondo as a linter. There are good instructions how to use clj-kondo with IntelliJ IDEA. With IntelliJ integration clj-kondo is a really good tool you should use with IntelliJ + Cursive. There are good instructions also for Emacs integration. I tried clj-kondo also with Emacs and it was pretty easy to install and use.

Unit Testing

To run the unit tests in command line use: run-tests.sh, example:

./run-tests.sh env-dev-single-node

Using IntelliJ IDEA + Cursive and Emacs + Cider Interchangeably

I used IntelliJ IDEA + Cursive plugin when implementing this Clojure exercise but just out of curiosity I wanted to try how it feels like to use Emacs +Cider when programming Clojure. The experience was quite pleasant. I managed to configure Emacs with the same theme I use with IntelliJ (leuven — mostly the same colors for the same syntactic/semantic entities) and the same hotkeys to compile code regarding one S-expression or the whole namespace, run all tests in the namespace or just the test under cursor etc. It took some time and some googling regarding Emacs elisp but finally I was pretty satisfied: now I can quite effortlessly use either IntelliJ IDEA + Cursive or Emacs + Cider when developing Clojure code — and the look-and-feel is pretty much the same. Why? Well, e.g. you can use Emacs + Cider in headless environments where you don’t have GUI.

For a Clojure Learner

I hope this exercise is helpful also for someone learning Clojure. I have tried to provide some efficient Clojure programming practices and links to various resources you can find more information. Try to configure e.g. Emacs the same way I did, git clone this repository, load various namespaces and try to send the S-expressions in the scratch file for evaluation to REPL.

I’m a Software architect and developer. Currently implementing systems on AWS / GCP / Azure / Docker / Kubernetes using Java, Python, Go and Clojure.