7.5 Wrapping Up Clojure

Clojure combines the power of a Lisp dialect with the convenience of the JVM. From the JVM, Clojure benefits from the existing community, deployment platform, and code libraries. As a Lisp dialect, Clojure comes with the corresponding strengths and limitations.

The Lisp Paradox

Clojure is perhaps the most powerful and flexible language in this book. Multimethods allow multiparadigm code, and macros let you redefine the language on the fly. No other language in this book provides this powerful combination. That flexibility has proven to be an incredible strength. In Hackers and Painters, Graham chronicles a start-up that leveraged productivity with Lisp to achieve productivity that no other vendors could match. Some emerging consultancies are taking the same approach, betting that Clojure will provide a productivity and quality advantage that other languages simply cannot match.

Lisp’s flexibility can also be a weakness. Macro expansion is a powerful feature in the hands of an expert but will lead to unmitigated disaster without the proper thought and care. The same ability to effortlessly apply many powerful abstractions in a few lines of code makes Lisp especially demanding for all but the most skilled programmers.

To successfully evaluate Clojure, you need to look at Lisp but also the other unique aspects of the Java ecosystem and the new unique features. Let’s take a deeper look at the fundamental strengths of Clojure.

Core Strengths

Clojure is one of a handful of languages vying for the position as the next great popular language on the Java virtual machine. There are many reasons that it is a powerful candidate.

A Good Lisp

Tim Bray, programming language expert and superblogger, called Clojure a good Lisp in a post called “Eleven Theses on Clojure.”[23] In fact, he calls Clojure “the best Lisp ever.” I would agree that Clojure is a very good Lisp.

In this chapter, you saw Rich Hickey’s discussion on what makes Clojure such a good Lisp:

You might appreciate Lisp as a programming language in its own right. By that measure, you can look at Clojure purely as a new Lisp. On that level, it succeeds.

Concurrency

Clojure’s approach to concurrency has the potential to change the way we design concurrent systems completely. STM does place some additional burden on developers because of its novelty, but for the first time, it protects developers by detecting whether state mutations happen within appropriately protected functions. If you’re not within a transaction, you can’t mutate state.

Java Integration

Clojure has great integration with Java. It uses some native types such as strings and numbers transparently and offers type hints for performance. But Clojure shines by allowing tight JVM integration, so Clojure types can fully participate in Java applications. You’ll soon see much more of Clojure itself implemented within the JVM.

Lazy Evaluation

Clojure adds powerful lazy evaluation features. Lazy evaluation can simplify problems. You have seen only a taste of how lazy sequences can shape the way you attack a problem. Lazy sequences can reduce computation overhead significantly by delaying execution until it is actually needed or by preventing execution altogether. Finally, lazy problem solving offers just one more tool to solve difficult problems. You can often use lazy sequences to replace recursion, iteration, or realized collections.

Data as Code

Programs are lists. As with any Lisp, you can represent data as code. Working with Ruby has helped me see the value of writing programs in programs. I think this is the most important capability of any programming language. Functional programs allow metaprogramming through higher-order functions. Lisp extends this idea through evaluating data as code.

Core Weaknesses

Clojure is a language that’s firmly targeted as a general-purpose programming language. Whether it can actually be broadly successful on the JVM is yet to be determined. Clojure has wonderful abstractions but many of them. To truly embrace and use those features effectively and safely, a programmer will need to be highly educated and extremely talented. Here are some of my concerns.

Prefix Notation

Representing code in list form is one of the most powerful features in any Lisp, but there is a cost—prefix notation.[24] Typical object-oriented languages have a wildly different syntax. The adjustment to prefix notation is not easy. It requires a better memory and requires a developer to comprehend code from the inside out, rather than outside in. Sometimes, I find that reading Clojure pushes me toward understanding too much detail too soon. At best, Lisp syntax pushes my short-term memory. With experience, I’m told this improves. I’ve not yet turned that corner.

Readability

Another cost to data as code is the oppressive number of parentheses. Optimizing for people and computers is not at all the same thing. The location and number of parentheses is still a problem. Lisp developers lean heavily on their editors to provide the feedback for matching parentheses, but tools can never fully mask readability problems. Kudos to Rich for improving this problem, but it will still be a problem.

Learning Curve

Clojure is rich, and the learning curve is oppressive. You need to have an extremely talented and experienced team to make Lisp work. Lazy sequences, functional programming, macro expansion, transactional memory, and the sophistication of the approaches are all powerful concepts that take time to master.

Limited Lisp

All compromises have some cost. By being on the JVM, Clojure limits tail-recursion optimization. Clojure programmers must use the awkward recur syntax. Try implementing (size x) that computes the size of a sequence x with recursion and with loop/recur.

The elimination of user-defined reader macros is also significant. The benefit is clear. Reader macros, when abused, can lead to the splintering of the language. The cost, too, is clear. You lose one more metaprogramming tool.

Accessibility

One of the most beautiful aspects of Ruby or an early Java is its accessibility as a programming language. Both of those languages were relatively easy to pick up. Clojure places tremendous demands on a developer. It has so many abstraction tools and concepts that the result can be overwhelming.

Final Thoughts

Most of Clojure’s strengths and weaknesses are related to the power and flexibility. True, you might work hard to learn Clojure. In fact, if you’re a Java developer, you’re already working hard. You’re just spending your time on Java application-level abstractions. You are looking for looser coupling through Spring or aspect-oriented programming, for example. You’re just not getting the full benefits of additional flexibility at the language level. For many, that trade-off has worked. I will humbly suggest that the new demands of concurrency and complexity will continue to make the Java platform less and less viable.

If you need an extreme programming model and are willing to pay the price of learning the language, Clojure is a great fit. I think this is a great language for disciplined, educated teams looking for leverage. You can build better software faster with Clojure.

Footnotes

[17]

Star Wars Episode V: The Empire Strikes Back. Directed by George Lucas. 1980; Beverly Hills, CA: 20th Century Fox, 2004.

[18]

http://github.com/technomancy/leiningen

[19]

Microsoft’s common language runtime, a virtual machine for the .NET platform

[20]

http://www.infoq.com/articles/flightcaster-clojure-rails

[21]

More precisely, some returns the first value that is not nil or false. For example, (some first [[] [1]]) returns 1.

[22]

http://www.thinkrelevance.com

[23]

http://www.tbray.org/ongoing/When/200x/2009/12/01/Clojure-Theses

[24]

Clojure does have left-to-right macros, ->> and ->, which mitigate these problems a little.