Clojure is a functional, dynamic programming language which is getting more and more popular by challenging many "best practices" while keeping a very pragmatic approach to real-world problems.
It clearly diverges from the common ALGOL family languages like Ruby, C and Java. On the other hand, it tries to avoid the common pitfalls of other functional programming languages that have lead to them being perceived as impractical, "ivory tower" languages only used by researchers and purists.
This is a very ambitious approach, so let's have a look at our Tech Pick of the Week: Clojure!
Clojure on the JVM
The main platform for Clojure is the JVM. Functions are compiled to JVM bytecode and make use of Java's type system, garbage collection, threads, etc. This allows you to easily integrate Java code with Clojure code and vice versa. Clojure comes with a nice interoperability API to access the whole Java ecosystem from within your Clojure code:
(import 'java.net.URL) (URL. "http://www.futurice.com")
This imports the Java URL class and calls the constructor `new URL("http://www.futurice.com")`.
Java interoperability is well thought out, easy to use and keeps the whole Java environment at your fingertips while enjoying the benefits of Clojure. Additionally interoperability also works the other way around: You can provide interfaces from your Clojure code which then can be called from Java code easily.
.NET and Android
There is also a Clojure implementation on top of .NET CLR, called ClojureCLR. ClojureCLR is not just a half-baked port to another environment – it is maintained separately from the JVM version, and thus provides unique support and interoperability for its environment. Like Clojure on the JVM, ClojureCLR grants access to the whole .NET ecosystem.
Clojure also works on Android's Dalvik VM. With respect to memory consumption and startup time, Clojure apps still lag behind Java apps on Android. But again, thanks to Java interoperability, there are no restrictions on what you can achieve with it on Android.
The Sweet Spot for Clojure
The range of supported platforms allows to build a broad range of applications with Clojure, including desktop, server, mobile and browser applications. To facilitate this, Clojure has been built as a general purpose language. Furthermore, Clojure can be extended through meta programming via macros (as we will see later). You can extend and change the language to fit your needs, from small changes up to building an extensive Domain Specific Language (DSL). Still, you might be wondering what the "sweet spot" of Clojure is.
"Clojure's sweet spot is any application that has state."
Stuart Halloway, Clojure core member
That's a nice sales slogan, isn't it? Let's have a look at the remarkable features and characteristics of Clojure to see how it supports Stuart's quote.
Functional Programming and Handling State
Clojure supports first-class and higher-order functions. All data structures are (by default) immutable data types. Furthermore, Clojure encourages the use of pure functions. Clojure is thus well suited for functional programming.
But as was said above, Clojure also embraces pragmatic solutions to tackle real-world problems. Due to that, it’s – by design – not a strictly pure functional language. Instead, it allows for side effects and mutability in a convenient way – you just have to be explicit about it. Let's have a look at this next.
Handling application state in a unique way is at Clojure's core and explains Stuart Halloway's claim above. State in Clojure is a simple, immutable data structure like a scalar or a map. To reflect changes in state, you simply create a new, immutable state which then replaces the old state of an entity in the application.
These entities are represented by four different reference types in Clojure. The simplest one is called "atom", here is a simple example:
;; create and initialize an atom (def my-counter (atom 0)) ;; define a pure function which adds 3 to a given value (defn add-three [x] (+ 3 x)) ;; use the pure function to produce a new value and assign it as a new state (swap! my-counter add-three) ;; dereference the atom to read its current value @my-counter ;; => 3
The important idea here is that the identity "my-counter" gets a new state at a certain point in time. Once you recognize state as a point in time, you can see how immutable state is a much more useful abstraction than mutable state: the state of an entity at a certain time will never ever change, although the same entity might have a different state at other times.
In addition to atoms, Clojure offers three other reference types which are very useful when dealing with concurrency or changing states asynchronously.
Software Transactional Memory (STM) is how Clojure simplifies state for concurrent and parallel programming. It is similar to database MVCC and complies with the ACID properties Atomicity, Consistency and Isolation. Durability is not part of STM as it is an in-memory storage only.
Handling state is a substantial part of Clojure and I recommend interested parties to read more about it.
Metaprogramming via Macros
Clojure is based heavily on the Lisp programming language, and has excellent support for macros. Macros are a meta programming tool, allowing you to programmatically change your code during compile time. This lets you extend the Clojure language easily and make it fit your requirements.
Even if you don't use macros yourself, you will benefit from them as they allow you to write very sophisticated libraries. A good example is clojure/core.async, which facilitates asynchronous programming by implementing ideas from Golang's channels. Using macros, this library is able to extend the Clojure language in a fundamental, low-level way while keeping it well integrated with other language features. Macros allow for a very high level of abstraction in your design, reducing boilerplate as well as adding features to Clojure.
Tooling is a crucial aspect of new programming languages to facilitate its acceptance by a broad community.
Leiningen is an excellent solution to handle Clojure projects and their dependencies. Furthermore, it handles compilation, packaging, running tests and much more. It is a one stop solution and feels very lightweight compared to more traditional Java project management.
Editor and IDE support for Clojure is already good and is constantly improving. Emacs has a very strong background in Lisp-based languages and as such offers very mature solutions. Vim also has very good Clojure support. There are good plugins for Eclipse and IntelliJ is about to get the Cursive plugin. If you’re more interested in the bleeding edge, Lighttable offers some new, unique ideas about how IDEs work and is packaged with native Clojure support.
Clojure in the Wild
How about Clojure's maturity? It was released in 2007 and the latest version is 1.5.1, released in March 2013. Thoughtworks put Clojure into the "adopt" state of their techradar since Oct 2012. Clojure is within the top 20 most used languages on github, but it is listed only at #76 in the Tiobe Index.
Many well-known companies use Clojure, including Akamai, Amazon, Nokia, Soundcloud, Twitter (BackType), Disqus, Daily Mail and Heroku.
The language itself is stable and mature. Clojure's ecosystem and community are growing and lively. Some very interesting projects can be found in the Clojure world, like the Datomic database, the Pallet DevOps toolset, Storm (distributed real-time computation), logic programming with core.logic and the music synthesizer Overtone. Web development as well as Data Science (including Big Data stacks) are also very well covered by mature, high-quality Clojure libraries. Examples include ring and compojure (web stack), liberator (REST services), cascalog (data processing with Hadoop) and incanter (R-like statistics).
There are, of course, some potential downsides to using Clojure. Debugging from stack traces can be a bit difficult due to Clojure's internal Java classes and all the anonymous functions that are involved. Debugging tools for Clojure are getting better, though. Namely ritz is currently considered a very good debugging solution.
However, when talking about debugging, one has to keep in mind that most Clojure developers have quite a different workflow than Java developers. For example, with Clojure, you are using a read-eval-print-loop (REPL) in conjunction with an editor to develop your programs interactively. You share code between your source files and the REPL. Often you write and test a function in the REPL first and then copy it to your source file. With respect to debugging, this means you usually find errors easily since you just modified the function in a REPL and invoked it straight away.
Although the Lisp syntax might feel strange on first contact, Clojure itself is not hard to learn at all. On the other hand, if you are only used to imperative and/or object oriented programming, functional programming will take some time to learn. This also means there doesn’t exist a huge pool of cheap developers capable of writing Clojure (yet).
But in the end, learning Clojure is a worthwhile investment in a developer's expertise. Learning how to write concise, robust, extendable and maintainable programs can hardly be a downside, right? So give Clojure a try and check out if it helps you to achieve these essential goals that we software developers strive for.
Getting started and additional Reading
- Clojure Homepage, official Clojure Rationale
- Clojurescript on github
- Dependency and Project Management: Leiningen
- Try and learn Clojure online by exercise: 4Clojure
- Collection of Clojure exercises: Clojure Koans
- Try Clojurescript online: Himera