<-- home

Clojure's Little Things

For the last 8 months, I have been writing primarily Clojure and ClojureScript code at Altometrics. At my previous job, I worked on a Ruby on Rails app. There are many features offered by Clojure that have proven its merit to me including the big picture, "killer" features: functional programming, immutable data structures, STM, macros, ClojureScript... But aside from those, there are other, smaller things that make Clojure a delight to use. I thought I would list a few little things in Clojure that I would miss if I was using a language that lacked them.

Docstrings

In Clojure, the programmer can choose to add an optional documentation string when defining a function. Here's an example from Clojure's standard library:

    (defn every?
      "Returns true if (pred x) is logical true for every x in coll, else
      false."
      {:tag Boolean
       :added "1.0"
       :static true}
      [pred coll]
      (cond
       (nil? (seq coll)) true
       (pred (first coll)) (recur pred (next coll))
       :else false))

Every function in the Clojure standard library has a docstring that explains how to use it. Then in a Clojure REPL, it is easy to print the docstring of any function with the doc function.

    user> (doc every?)
    clojure.core/every?
    ([pred coll])
      Returns true if (pred x) is logical true for every x in coll, else
      false.
    nil
    user>

In the Ruby world, the state of the art in documentation is writing your method documentation in regular ruby comments, then manually running a program like rdoc to turn your doc comments into HTML documentation pages, and then hosting that documentation somewhere. That's a significant amount of effort for the library writer, and the end result is docs the users have to view in a web browser instead of a REPL!

Because docstrings are a first class citizen in Clojure, and because of the simplicity of typing (doc function-name) in a REPL, nearly everyone in the open-source Clojure community adds docstrings to their API functions. This standardized documentation workflow yields a better experience than looking at documentation scattered around Github for each library you use.

Explicit Require

In Ruby, if I want to call code that exists in a different file, I must use a require statement at the top of my file.

    require 'application_controller'
    require 'post'

    class PostsController < ApplicationController
      def index
        @posts = Post.all
      end
    end

The line require 'post' grants me access to the Post class so that I can call Post.all, and the line require 'application_controller' gives me access to the ApplicationController class. But how did I know which classes were imported from which require statement? The truth is that don't know; I just guessed based on naming convention that the Post class came from the post file and vice versa.

Rails actually takes this one step further with a feature called autoloading which obviates the needs for require statements completely, allowing any file to basically use any top level constant from any other file, including every third party dependency your app uses. The end result of this design decision is a tendency towards "Grep Driven Development", where in order to determine where any code is defined the programmer often has to resort to running grep on the project directory.

In Clojure, code is separated into namespaces. Each namespace includes a namespace declaration that includes a list of things to require from other namespaces.

    (ns my.namespace
      (:require [clojure.string :as str]
                [clojure.set :refer [union subset?]]))

In this namespace I want to use functions from Clojure's standard string and set libraries. For clojure.string I used the :as option to give the namespace an alias str. Then in my code in my namespace, I can call functions from the string library by prefixing the function name with my alias: (str/capitalize "wyoming"). There's no ambiguity about where the capitalize function is defined.

I chose to use the :refer option in my require line for clojure.set. With :refer I can add a list of functions that I want to be able to call without a prefix: (subset? #{1 2} #{1 2 3}). Because we enumerate the functions we want from the clojure.set namespace, this also avoids ambiguity.

My example is a subset of options available in Clojure require call. There are more options to get increasingly fine grained control.

Compilation Step

The default implementations of Clojure and ClojureScript are compiled, rather than being runtime interpreted. In the compilation process, the Clojure compiler verifies that all symbols can be resolved to something. So in Clojure if I typo a function name:

    (defn increase-num [num]
      (+ num 1000))

    (increased-number 23)

my program won't compile:

    clojure.lang.Compiler$CompilerException: Unable to resolve symbol: increased-number in this context

By contrast, MRI Ruby is an interpreted language, where method calls are resolved at runtime. So if I call a method that isn't defined (which could happen because of a typo, or because that method was removed from under me) I won't see any error until that code is actually executed, potentially after I've already released or deployed my broken code.

The peace of mind that a compilation step brings to code refactor is hard to overstate.

Conclusion

These are just a few Clojure things that came to mind. I also believe that programming languages that I haven't yet used might one day introduce me to new "must-have" features that aren't yet on my radar. Hit me up on Twitter if you have another example of a little language feature that's important to you from Clojure or otherwise.

- Adam Frey, February 2016

<-- home