Clojure for the Javascript Programmer

What follows is the first entry in what I imagine will be a short series about Clojure, all from the perspective of a someone who writes a lot of Javascript at his day job. This is intended to be a simplified introduction to Clojure. It will become a short presentation to interested engineers where I work. I’ve deliberately simplified and omitted some detail that would be covered by a more-comprehensive resource.

I’ve become a bit infamous among my peers for evangelizing Clojure and the unfamiliar concepts it makes use of. I’m especially interested in ideas that can be ported to more-familiar languages because I believe they make it easier to think about complex software. I believe the easiest way to learn those concepts is to become at least passingly familiar with Clojure.

Clojure can be an intimidating beast from a distance, but up-close it’s not nearly as terrifying. A recent comment by a coworker is summarized by, “Clojure seems really powerful, but when I look at that code I’m totally lost.” I believe this sentiment is rooted in Clojure’s unfamiliar syntax, but the language described by that syntax isn’t very much different from Javascript. Brenden Eich based a lot of Javascript on Smalltalk, which was itself inspired by Lisp. Clojure is a Lisp dialect, so the two languages are distant cousins of one another; while the syntaxes of each language might be wildly different, many of the underlying ideas are the same.

A quick language primer

There are two rules for translating between Javascript and Clojure. The first rule is that parenthesis begin to the left of the function name:

capitalize("hello world")  // => "Hello World"
(capitalize "hello world") ;; => "Hello World"

You can see that the function named capitalize is enclosed by parenthesis, rather than followed by them.

The second rule is that everything is a functional expression. Even mathematical operators are defined as functions, and are enclosed by parenthesis:

2 * 4         // => 8
(* 4 2)       ;; => 8

2 * 4 + 1     // => 9
(+ (* 2 4) 1) ;; => 9

This looks awkward at first, but has the advantage of never being ambiguous about operator ordering.

Clojure has a bunch of built-in literals to describe different types of data, almost all of them should be familiar from Javascript:

  • true, false, nil - The values true, false, and nothing are pretty simple
  • 1000, 1.25 - Numbers look the way you’d expect 1
  • "Hello world" - So do strings
  • :nietzsche - This is a new one. This is called a keyword, and it’s a placeholder value that always means itself. It’s similar to the Symbol type in Ruby, or a Java Enum value. It provides fast equality checking and has a few other uses throughout the language.
  • [ :oranges, :bananas, :pineapples ] - This is a vector of keywords. A vector is basically an array.
  • { :name "Rich Hickey", :employer "Cognitect" } - This is a map. Note that the keys here are keywords, which aren’t strings. Maps in Clojure are like Java-style maps, in that the keys can be of any type.
  • #{ :oranges, :bananas } - This is a set. It’s like a vector, but has only unique items.
  • (fibonacci 5)

The last peculiar thing about Clojure is that it doesn’t reserve very many characters for syntax purposes. The following are all valid names in Clojure: number?, transact!, assoc-in, clj->json, *app-state*, >!.

Why learn Clojure?

“Clojure feels like a general-purpose language beamed back from the near future.” This is how the book Programming Clojure describes the language. Clojure has a constellation of features that will help you build better programs:

The most striking feature to newcomers to the language is how expressive it is. A Clojure program can easily be half as long as the equivalent in other languages, and often even smaller than that. Once you’re comfortable with the standard library, it will be hard to go back to more-verbose languages. Here are two snippets in Javascript that check if an array contains only numbers:

// Naive Javascript:
function areAllNumbers(collection) {
    for(var i = 0; i < collection.length; i++) {
        if(typeof collection[i] !== 'number') {
            return false;
        }
    }
    return true;
}

// Or, using Javascript's collection methods:
collection.every(function(item) { return typeof item === 'number'; });

The following examples are the same algorithms in Clojure; Even if you’re unfamiliar with the language, it should be possible to see the “bones” of what’s going on:

;; Naive Clojure:
(defn all-numbers? [coll]    
  (cond
    (empty? coll) true
    (number? (first coll)) (recur (rest coll))
    :else false))

;; And using the Clojure collections library:
(every? number? collection)

Javascript stands out as a very expressive language, and yet the Clojure samples remain significantly smaller. This disparity is only magnified as programs become more complex.

There is an important distinction between conciseness and terseness. Minification or creative “code golf” can make the source code smaller, but at the cost of understandability. This is what I mean by terse. Conciseness is about being able to express an idea intelligibly in a small space, such that it can be composed easily into higher-level concepts.

The next obvious feature is that it’s a functional language, which can be a tough thing to get used to. Much of Javascript is calling functions attached to objects to produce a new state. In Clojure, you call functions against values to produce a new value. If you want to append to an array in Javascript, you use the concat method with the value you want to add, in Clojure you call the conj (short for “conjoin”) with the collection and the value you want to add.2

[1, 2, 3].concat(4); // => [1, 2, 3, 4]
(conj [1, 2, 3] 4)   ;; => [1, 2, 3, 4]

This is also a good time to bring up the excellent standard library Clojure has. Rich Hickey, the author of Clojure, is allergic to re-implementing the same functions for different data types, and the standard library reflects this.

The Clojure standard library is large, but can be used across many data types. The biggest beneficiaries of this approach are collections. Every collection in the language can use the functions that operate on sequences (including strings and maps). The values of the collections are intelligibly coerced into a shared representation that can be easily acted upon:

(first [ 1 2 3 ])         ;; => [ 1 ]
(first { :foo 1 :bar 2 }) ;; => [ :foo 1 ]
(first "Hello")           ;; => \H
(first #{ :red :green })  ;; => :red

I believe most-important feature of Clojure to understand is immutability. In Javascript, strings are immutable—there’s nothing you can do to a string that won’t create a new string as a result. If you call .toUpperCase() on a string, it will return a new copy of that string with all the letters in upper-case, rather than modifying the string in place. In Clojure, all data behaves in this way; If you want to add an item to a vector, you call conj, which will return a new vector with the item added.

Why is this important? There are a few reasons: It means that the functions you write will usually be pure (have no side effects), which will make them easier to test and reason about. It makes sharing data throughout your program extremely safe, because distant parts of your application can’t alter the states of each other accidentally.

For example, there’s a bug in the following code:

var phoneNumbersPromise = getPhoneNumbers('george');

phoneNumbersPromise.then(function(phoneNumbers) {
    phoneNumbers.forEach(function(it) {
        it.type = normalizeNumberType(it);
    });

    renderAllPhoneNumbers(phoneNumbers);
});

phoneNumbersPromise.then(function(phoneNumbers) {
    var hasHomeNumber = phoneNumbers.some(function(n) {
        return n.type === 'HOME'
    });

    if(hasHomeNumber) renderHomePhoneNumber(phoneNumbers);
});

The behavior of one promise block depends on the behavior of the other. It doesn’t matter what normalizeNumberType does, it affects the value of the phone number in a way that the second resolver will succeed or fail depending on execution order. Because we’re dealing with asynchronous code, execution order may be non-deterministic. These two resolvers might be separated by tens of thousands of lines of code, or may have been added at different moments during development.

If the value returned by the phone number promise were immutable, this bug could not exist.

In addition to the above major features, you get a bunch of pretty awesome minor ones:

  • Live coding
  • Managed CSP
  • Homoiconic code
  • Macros
  • ClojureScript
  • Easy testability
  • JVM / Javascript interop
  • Value destructuring
  • Ad-hoc Polymorphism
  • Lazy and Infinite Sequences

I believe there is value in learning new languages with unfamiliar ideas. Every programming language that’s made it to production has been a successful attempt at solving a problem in software. The ideas encoded in these languages are often portable to other languages, or can be an introduction into new ways of thinking about a particular set of problems. I believe that the concepts embedded in Clojure are incredibly useful, and they will make you a better programmer in whatever language you choose to write in.

If you’re looking to get started in Clojure, I highly recommend the book Programming Clojure by Stuart Halloway. From there, you can either try out some practical Clojure with the Basic Om Tutorial, which covers building React-style UIs in ClojureScript, or you can try out the challenges on CodeWars, which has a large-and-growing collection of Clojure challenges.

  1. Clojure numbers also support a rational form. So if you divide 2 by 3, you’ll get a rational value of ⅔ rather than the less-accurate 0.66̅. ClojureScript does not have rational numbers and use the Javascript numbers instead.
  2. The common function to use in Javascript would actually be push, but push and conj aren’t strictly equivalent. push alters the array in place and returns the value you pushed into it. concat combines the values you give it into a new array and returns that value. As a style note, I tend to avoid push unless it makes my code significantly more complicated.

What’s next?

A few days ago, the development team behind YUI announced that development on the project would be halted. I didn’t think I had anything to say about it—I assumed that I would feel some sadness for a few weeks or months until I moved onto whatever is next. But it’s been a week, and I think I’m itching for something to say.

I’ve also been looking for a reason to start blogging again, and this is a good a reason as any. In the wake of the “death” of YUI, what now? What’s good out there? What are the bad ideas? I wonder if there’s anything I can contribute to the conversation.

The world of JS has changed a lot in the last five years. The old-and-busted IE world is fading and the future looks like a bright forest of evergreen browsers. Node.js came out of left field; it now completely dominates the frontend build-tooling world and has a growing place as the frontend of the backend. There are now enough popular compile-to-JS languages that whole communities have sprouted up around each. It’s now possible to compile a C++ game engine to something that can run on the browser. The modern web is a weird place, I hope to make a little bit of sense out of it in the process.

I suspect that I’ll be writing about three different topics in the coming months:

  1. Exploring how to build large applications using new frontend technologies.
  2. Demonstrations of interesting new or “fringe” programming constructs
  3. Looking at the behavior and implications of language features

Before I start looking forward, I want to begin by looking back.

A retrospective on YUI

I’ve been building applications in YUI for my entire professional career. YUI2 was the first library I picked for ajax, animations, and widgets. I tracked the early development of YUI3 with great interest. Learning YUI3 threw me into entrprise-quality javascript with composable primitives, great documentation, comprehensive testing, and readable source code.

YUI was never a very sexy tool to work with. For most of the last 5 years, it was owned by Yahoo, which despite its great in-house talent, doesn’t project an aura of developer credibility, especially over the last few years. The YUI branding was boring and didn’t clearly advertise what made it special. The value of the framework was also hard to articulate to fellow UI developers; In 2010 extensibility, composibility, and maintainability took a back seat to simple APIs and fast DOM mutation. For almost everyone, the features of the library appeared monolithic and over-engineered. I believe history has validated YUI’s ideas, though—in 2014, most of the ideas first pioneered by YUI exist in current client-side frameworks.

To its fans, YUI felt like being in a near-future world where browser inconsistencies were gone, the event API was robust and sane, modules could be isolated and tested, and building applications with complicated state wasn’t a nightmare of DOM smashing. If a component didn’t do exactly what you wanted it to, almost anything could be extended and modified. Compared to black-box jQuery plugins that were its contemporary, YUI is a revelation in composability.

The community was similarly execellent. I used to monitor the message board, then the mailing list, and still spend much of my day lurking on IRC. I feel strongly that the quality of a community can be measured by the responses to open-ended design questions (“What’s the best way of doing x?”). There was always a constructive discussion about the most-maintainable way to achive a certain goal, even if it wasn’t in the “YUI happy path”. Asking a question about a new feature like promises or application routing was met with helpful examples, usually by the designers of those modules.

Unlike many modern JS frameworks, YUI isn’t “opinionated”1 about application structure or design. It makes a few recommendations, but the components were loosely-coupled and could be used in many arrangements. Prior to the announcement, there was discussion of breaking the framework up into ES6 modules that could be used outside the YUI context with limited dependencies. This would have been a significant-but-worthwhile project.

There’s still a lot of code that I’ll be working with that sits on top of YUI, but has been marked for refactoring to Angular2 since late last year. Eventually, the dependency will be removed entirely, and I’ll have few enough reasons to hit up the documentation. Still, I wouldn’t be the developer I am today without the experience I’ve had with YUI; the great ideas embedded in it will inform my decisions about writing software for a long time.

  1. “Opinionated” feels like a spin-word for a much uglier reality—an opinionated system is tightly-coupled or very complex, and usually both. It’s telling that systems that could reasonably be called opinionated, but also have well-justified reasons for their decisions, do not claim that adjective. On the other hand, systems that make design decisions without clearly-stated rationales are eager to use it.
  2. I assume I’ll be writing a lot about Angular in the coming months. I’m sure know too little about it, and I’m sure I dislike it too much.