Getting Productive With Clojure

Dec 30, 2018

I recently wrote my first real program in Clojure! (A simple HTTP based write though key value cache built on top of Zookeeper.)

What do I mean by a real program? In the past I've read books and written small toy scripts but I've never tackled problems similar to ones I might be solving at work.

The goal of this post is to share some things that got me from an empty directory to useful program in the hope that others might try the same. This isn't best practice or expert advice, but it worked for me!

Install Clojure

Clojure has a Getting Started guide for installation.

Use Leiningen

Leiningen is a program that takes care of creating and automating Clojure projects. There are other ways to get started with Clojure, but this worked really well for me! I installed Leiningen using my package manager but there are also installation instructions on the project's website.

I wanted to write an executable so lein new app <project-name> got me started. I proceeded to delete all the files that didn't seem relevant to me. (This will likely make some people cringe!)

$ lein new app my-project
$ cd my-project
$ rm CHANGELOG.md
$ rm LICENSE
$ rm -rf doc/
$ rm -rf resources/
$ rm README.md

I also went into the project.clj file and deleted the description, url and license lines.

  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}

Now we have a really simple directory structure that's easy to undertand and expain!

$ tree
.
├── project.clj
├── src
│   └── my-project
│       └── core.clj
└── test
    └── my-project
        └── core_test.clj

core.clj contains your executable's main function. core_test.clj contains your tests. You can run them respectively with lein run and lein test.

$ lein run
Hello, World!
$ lein test

lein test my-project.core-test

lein test :only my-project.core-test/a-test

FAIL in (a-test) (core_test.clj:7)
FIXME, I fail.
expected: (= 0 1)
  actual: (not (= 0 1))

Ran 1 tests containing 1 assertions.
1 failures, 0 errors.
Tests failed.

Better test output

I found that the default test output to be hard to follow for complex failures.

I created the following file in ~/.lein/profiles.clj.

{:user {:dependencies [[pjstadig/humane-test-output "0.8.3"]]
        :injections [(require 'pjstadig.humane-test-output)
                     (pjstadig.humane-test-output/activate!)]}}

Now the test output includes a diff:

$ lein test

lein test my-project.core-test

lein test :only my-project.core-test/a-test

FAIL in (a-test) (core_test.clj:7)
FIXME, I fail.
expected: 0
  actual: 1
    diff: - 0
          + 1

Ran 1 tests containing 1 assertions.
1 failures, 0 errors.
Tests failed.

Running a single test

Kinda verbose but whatever.

$ lein test :only my-project.core-test/a-test

No you don't need to learn Emacs or setup a fancy REPL

There's lots of talk in the Clojure and Lisp community of fancy REPLs, using Emacs and SLIME. (I've used SLIME before and it's very cool!)

You don't need it to get started!

My development flow looks something like this:

I want to explore more fancy REPL stuff in the future, but this got me productive!

Multiple files

I wanted to factor my code into multiple files and was having trouble figuring out how to create multiple namespaces and all this fancy stuff. Here's something super simple to get started!

(ns my-project.core)
(load "app")

Multiple namespaces are probably the way to go for larger projects, but this worked great for me!

Using Libaries

Let's add the Ring HTTP library as an example.

Start by adding the dependency to your project.clj file.

  :dependencies [[org.clojure/clojure "1.10.0"]
                 [ring/ring-core "1.7.1"]]

Then download the dependency. (Running lein test or lein run should do the the same thing.)

$ lein deps

We can then require the functions we need in our file's namespace definition.

(ns my-project.core
  (:require [ring.middleware.json :as ring :refer [wrap-json-body]]))

You can can use the function with ring/wrap-json-body.

Useful Libraries

Here are some useful libraries. I've used them all in this project so that may be a good place to see how they're used.

HTTP

Command line parsing

JSON

Zookeeper

Likely not relevant but I used zookeeper-clj as a Zookeeper client.

Useful Features, Macros and Functions

Documentation

Use (doc <function-name>) from the REPL for quick documentation lookup.

Experience

A few things I don't like:

Things that were great:

I'm generally pleased with the experience!

Index