Clojure, I Choose You!

Ash Ketchum is ready to unleash Clojure to best his current software challenge.

As a software development company, choosing the right tools for the job is of paramount importance. In this post, we will explain why we opted to use Clojure for the full web stack of our platform. When building complex software, leveraging a language that not just supports but actively promotes good engineering practices provides key benefits in terms of efficiency, readability, maintenance and robustness.

What is Clojure?

Clojure is a modern member of the Lisp family. To most people, a lisp is a speech impairment characterized by misarticulating sibilants, that may result in unclear speech. To programmers, Lisp, an acronym for list processing, is a family of programming languages that is notorious for its fully parenthesized prefix notation, that may result in double vision.

Interestingly, Lisp is the second-oldest high-level programming language that still enjoys widespread use today. Lisp was specified in 1958 and is outdated only by Fortran. If this doesn’t convince you that it’s highly suitable to build complex modern web apps using cutting edge frameworks, perhaps the remainder of this post will.

Clojure was designed to be a language that is broadly applicable but simultaneously supports a simple programming model. Like all Lisps, Clojure features minimal syntax: each expression is written in parenthesized prefix notation. As a syntax example, the snippet below computes the sum of a and b.

(+ a b)

Lisps are somewhat of a niche in today’s software development landscape, with many considering it to be a family of toy languages that is unfit for day-to-day software development, while others evangelize it to be the best thing since sliced bread. Given the title of this post, guessing which side we lean towards is left as an exercise to the reader.

Some statistics

We like statistics as much as the next data analysis company, so we include some interesting insights from StackOverflow’s 2019 developer survey.

A first subjective - but nonetheless relevant - element worth considering is how developers feel about various languages. Figure 1 shows which languages are most loved by developers already using them, and which languages they want to adopt. The survey shows that Clojure is highly appreciated amongst its users, but nonetheless remains an unsung hero with few other developers looking to adopt it.

StackOverflow stats on love/wants Figure 1: fraction of developers using the language that have expressed interest in continuing to use it (left) and fraction of developers not using the language that have expressed interest in using it. Figure taken from StackOverflow’s 2019 Developer Survey Results (link).

Interestingly, in addition to being a niche language, Clojure appears to attract some of the most experienced developers in the entire survey, as illustrated by another result, shown in Figure 2. The group of Clojure responders in the survey are among the most experienced in terms of both years of professional programming and salary. Moreover, Clojure developers reported above average salaries even after correcting for years of experience, which indicates their value as perceived by employers to a certain extent.

StackOverflow experience vs salary per language Figure 2: average years of professional programming experience (seniority) versus salary per programming language. Figure taken from StackOverflow’s 2019 Developer Survey Results (link).

In summary, the survey indicates that Clojure is considered a highly effective language by its current users - on average the second most experienced group of developers surveyed by StackOverflow (after F#) - but nonetheless receives little attention from people not already using it. One can’t help but observe the historical parallels with Lisp, which Paul Graham* considered to be the secret weapon to the success of one of his companies in his famous blog post titled “Beating the Averages”.

(*) You may remember Paul Graham from such incubators like Y Combinator.

Why we love Clojure

We will highlight some key features we deeply appreciate about Clojure for our work. This is by no means an exhaustive list of all interesting aspects about the language.

Simplicity

Clojure was designed to be a practical functional language to tackle real-world development tasks. It aims to empower developers by enabling them to focus on what matters, i.e., code directly related to the problem domain, while lowering the cognitive burden required for everything else. A simple programming model can go a long way towards these goals.

Clojure programs tend to be shorter and faster to develop compared to other languages, an advantage that is commonly mentioned for all Lisps, and a widely decried disadvantage of Java, Clojure’s first and most common host language.

Keeping a language simple is no small feat, and in my opinion, is a testament to the quality, discipline and vision of Clojure’s stewardship. Another notable modern language obsessively focused on maintaining a simple, minimalist programming model is Go which, unsurprisingly, has gained significant traction.

Simplicity is the art of hiding complexity. — Rob Pike (author of Go), 2015

Language stability

Rich Hickey, Clojure’s author and benevolent dictator for life, strived to make Clojure a stable tool for developers and therefore shunned breaking changes. Since its inception, the language itself has been impressively stable, with minimal breaking changes. This strategy has been widely applauded by Clojure users, and is encouraging for anyone considering to adopt the language.

An additional interesting fact is that, not just the Clojure language, but also its core libraries have seen few major reworks. An illustration thereof is shown in the code retention graphs below, depicting Clojure’s codebase (Figure 3) and that of Scala, another popular JVM-based language (Figure 4). A conclusion we like to draw from Figure 3 is that few overhauls have been necessary, which attests to good design choices from the get-go.

HoC Clojure code retention Figure 3: Introduction and retention of code in the Clojure codebase. This figure was taken from Rich Hickey, A History of Clojure (2020).

HoC Scala code retention Figure 4: Introduction and retention of code in the Scala codebase. This figure was taken from Rich Hickey, A History of Clojure (2020).

Functional first

Clojure differs from most of its Lispy family members in the fact that it explicitly embraces functional programming as the idiomatic style, while its relatives remain neutral. In Clojure, the bread and butter functions that make up programs receive and return immutable values by design.

Moreover, collections have high performance implementations of a mechanism called structural sharing to efficiently create derived collections in pure functions, rather than mutating the original collection and inducing side effects. For instance, you cannot mutate a basic map (dictionary for Pythonistas), but you can easily create a new map based on the original.

(let [original {:foo :bar}
      derived  (assoc original :ham :eggs)]
  (print original derived))

 ; prints {:foo :bar} {:foo :bar :ham :eggs}	

Note that the original map is unmodified. 

Immutability by default provides immediate practical benefits by preventing side effects, which avoids an entire class of bugs with minimal performance overhead owing to structural sharing. 

Moreover, pure functions remove a lot of complexity for developers, because there is no need to account for any external context to fully understand and test the function’s purpose and implications. In contrast, OOP languages heavily rely on side effects to implement functionality, i.e., by mutating one or several objects.

Code is data

A Clojure program is turned into Clojure data structures during parsing, a feature it shares with other Lisps. These data structures can be traversed and manipulated just like any other “business” data structure, which gives Lisp unparalleled flexibility, i.e., to make code that manipulates code.

Despite it’s borderline ascetic minimalism in core syntax, Lisp is flexible enough to transcend typical programming paradigms like object-orientation. For example, the object-oriented paradigm, including any optionally associated static typing system, can be implemented in Lisp.

Full stack

Clojure is designed to be a hosted dynamic language that can be efficiently compiled down to the native bits and pieces of its host platform. This makes it easy and efficient to interoperate with all libraries that are accessible via the host language and vice versa, without needing explicit conversions to and from Clojure.

What’s key for our use-case is that Clojure can be hosted on the Java Virtual Machine (JVM) and compiled down to JVM bytecode, thus providing direct server-side access to the cornucopia of frameworks and libraries that is available in the Java ecosystem. Moreover, ClojureScript is a variant of Clojure that targets the JavaScript ecosystem. The ClojureScript compiler emits JavaScript code that can optionally be optimized by the Google Closure compiler to very efficient and minimized JS code.

NOTE: The Google Closure compiler and the Clojure language are completely different projects that have no direct relationship. This homophonic curveball is just to keep you focused.

Owing to Clojure’s hosted design and its support for both the JVM and JavaScript, we can work full stack using all available libraries in Java and JS without ever removing our Lisp-colored glasses. The tandem between Clojure and ClojureScript enables us to leverage the same language and constructs for server-side and client-side code, despite the major differences between both platforms (i.e., web server vs. web browser).

glasses off

Being able to use Clojure for full stack development provides a number of benefits. Notably, this eradicates the entire class of bugs related to impedance mismatches between front -and back-end. Some other advantages include:

  • Sharing code, keywords, specs and domain models without overhead or duplicate effort.
  • Full stack development in a single project, leveraging the same IDE and largely shared test suites, without having to duplicate related functionality in different languages.
  • Out-of-the-box data serialization using Extensible Data Notation (EDN), Clojure’s native serialization format.

Conclusion

In this post, we’ve touched upon some of the key reasons why we chose to use Clojure for a significant portion of our technology development. Clojure is a modern Lisp variant that embraces functional programming. Clojure can be hosted on multiple platforms, notably the JVM and JavaScript, which enables us to use it throughout our full stack for fun and profit.

Lisps have been proclaimed to be the language from which the Gods wrought the universe. Regardless of whether you’re a believer or wrong, Lisps in general and Clojure in particular offer some neat features that are worth knowing about.