Recently, I’ve been learning about Clojure, and I have to say I’m impressed. I just started to learn about the language in my spare time, but my impression is very positive; all the different pieces seem to just magically interact together such that the sum of the parts is larger than the individual parts.
In this blog post, I will try to give you an idea of why I feel this way. Worth mentioning is that my programming background is C, C++, Python and Ruby (in that chronological order), gracefully ignoring the Basic I learnt when I was young. My impressions come from reading a book, solving some toy problems online and attending a conference, the EuroClojure 2013.
Without further ado, here are Clojure’s features at a glance:
- It’s a Lisp. It has an unfamiliar syntax with lots of parentheses for most programmers. I believe this initial impression is the main reason many refrain from learning the language.
- However, diverting from the pure functional programming paradigm, it allows you to manage mutable state with side-effects. It is idiomatic Clojure to have very little mutable state in your program (if at all), and the language encourages this by making all mutable state and all side-effects explicit.
- To manage mutable state, Clojure makes it explicit if, for example, two identities need to be changed together, in a transaction. The classic example is to move money from one bank account to the other. Behind the scenes, Clojure implements software transactional memory (STM), which guarantees all updates are atomic, consistent and isolated. (That’s like the ACID properties known from databases, without the durability, which is what the D stands for.)
- This plays along very well with concurrency. In Clojure the programmer doesn’t deal with locks. Instead, Clojure provides a set of abstractions that you can use to run things in parallel (like futures and delays). This plays along very well with immutable data structures (see below) and managed state, since you basically never have to deal with conflicts.
- Clojure embraces has built-in support for common data structures, like lists, sets and maps. It abstracts the processing of data using sequences. It is idiomatic to use lazy sequences, and since all aforementioned higher-order functions return them by default, their usage becomes natural. Lazy sequences represent data in a sequence, but the actual realization of values inside of that sequence happens only if these values are read/used somewhere.
- Clojure is dynamically typed.
- Data structures are immutable by default. If you want to add an item to a list, for example, what you conceptually do is not to modify the existing list in place, but rather create a new list having the added item. That sounds tremendously wasteful, but internally Clojure uses persistent data structures, which is basically a smart technique to avoid copying everything when creating a new value from an old one. In this way Clojure has an implicit notion of time, by making it possible to access past values of an identity.
- It runs on the JVM. Clojure itself is written in Java (and Clojure), and at its core is basically a jar file.
- Excellent Java interoperability. As such, you basically have immediate to all Java APIs.
- Clojure programs are, considering the expressive power of the language, fast. Depending on what and how you measure, you’ll roughly get performance which is only slightly slower than Java’s. Compared to scripting languages like Python or Ruby, that’s much faster.
- As a Lisp, it is homoiconic. That is, Clojure code is expressed in terms of Clojure data structures. This makes it easy to write code that modifies other code, which is basically what macros are about. This allows to provide features through libraries that other languages could only provide by extending the language itself.
- core.logic is a library that introduces logic programming to Clojure, i.e. Prolog-like relational, constraint-based programming.
- core.async adds facilities for asynchronous programming through channels. Analogous to go routines from the Go programming language, only implemented as library.
- Another impressive example is core.typed, which provides type checking if you want it.
One important thing about a new language is the culture that surrounds it. I believe in the case of Clojure, the culture is mainly centered around the creator of the language, Rich Hickey, and the people “surrounding” him, that recently founded Cognitect. I strongly recommend to view Rich Hickey’s greatest talks. I’d say they are almost a required viewing for programmers, even if you’re not interested in Clojure at all – there’s a reason the post was on top of Hacker News for quite some time.
I’ll try to give my current impression of a few trends/beliefs in that culture:
Skepticism about object-oriented programming. The atomic composable unit in a program are pure functions, not mutable objects. Clojurians always prefer generic data structures like maps, sets and lists over concrete objects.
A focus on well-thought out program/system design instead of ad-hoc designs that arise from patterns like test-driven development. In Clojure, you can do so much with just one line of code, that it becomes much more important to think thoroughly before starting to code. As such also skepticism about agile programming when it drives people to rush into coding without thinking first.
Separation of identities and values. Values are immutable pieces of data; identities are basically names for entities, which may change their values over time. Traditionally, programming languages complect these notions, since both of them are variables or objects.
In Ruby, the BDD workflow to follow is (I’ll simplify here): write tests, write code, execute test, reiterate until tests are green, go to next feature. Clojure doesn’t have such a focus on tests, instead much of the testing is basically moved to the REPL in the “ideal” workflow. See this blog post for details.
Belief in showing not only the positive parts of a solution, but also the negative parts. There is always a tradeoff, so one should consciously choose the best solution. For example, Clojure itself naturally also has negative parts: it’s not very well suited for small scripts, since the JVM takes a long time to load up. Moreover, it requires a shift in the mindset if you come from an object-oriented, imperative background. It requires some practice, you cannot just jump in so easily.
Libraries and Related Stuff
- Datomic is a closed-source database, designed by Rich Hickey. It draws quite some attention in the Clojure community, since it has been designed by the creator of the language, and, because Datomic brings to databases what Clojure brought to Programming.
tl;dr View Rich Hickey’s talks.