Catch up on stories from the past week (and beyond) at the Slashdot story archive


Forgot your password?

Programming Clojure 109

eldavojohn writes "Programming Clojure by Stuart Halloway was very near to the perfect book for me. It covers many things common to many Lisp languages while highlighting in moderate detail the things that make Clojure unique and worthy of some attention. The book spends a large amount of time dealing with the intricacies of interfacing fluidly with Java (down to a package rewrite inside a large project). This fits me perfectly as a Java programmer, and I now feel ready to experiment with peppering functional language capabilities into an object oriented language. The book also strives to show how to simplify multithreading through functional programming, which is good because I find multithreading in Java a serious headache that few are good at. Programming Clojure, released in May 2009, is currently the only book out there devoted to Clojure, and the introduction is written by the language's creator, Rich Hickey, who says, 'What is so thrilling about Stuart's book is the extent to which he "gets" Clojure.' The book earns its place on the Pragmatic Bookshelf by guiding the user through rewriting a part of Ant into a new build tool called Lancet — adding to the project what you just learned about Clojure at the end of each chapter." Keep reading for the rest of eldavojohn's review.
Programming Clojure
author Stuart Halloway
pages 304
publisher The Pragmatic Bookshelf
rating 8/10
reviewer eldavojohn
ISBN 978-1-934356-33-3
summary A firm definition of Clojure via examples coupled with the beginnings of actually programming Clojure.
First, a lot of you are probably wondering what Clojure is and asking me why you should care at all about it. Well, Clojure is a functional programming (FP) language that runs on top of the extremely pervasive Java Virtual Machine and in doing so seems to offer a simpler way of multithreaded programming. It belongs to the family of languages that are Lisps and as a result this book covers a lot of remedial material that is common to other Lisp languages. If you're a serious lisp programmer, you'll be able to skip some of this book (the intro will guide you). Clojure has rarely been mentioned on Slashdot with the resulting comments revealing largely confusion or considering it a buzzword. It's going to be hard to write this review about the book instead of the language being that 99% of what I know about Clojure comes from this book. If you work through this book linearly, you must also use the command line read-eval-print loop (REPL) that, similar to Ruby's IRB, allows you to get hands on with Clojure and Halloway's examples.

Both Hickey and Halloway are very active in Clojure development. In fact, Halloway has a video out on types and protocols, new developments in Clojure 1.2 since the book went to print. Halloway does a good job at providing examples, keeping the book pragmatic and showing you the "wrong" way before incrementally showing you how to correctly accomplish various goals in Clojure. But he loses two points on this review for two reasons. One is that he over evangelizes about Clojure. It would lend a lot more credibility to everything else he says if he would just relent and abstain a bit from painting Clojure as the best language for any task. This ties into my second point which is the fact that books on programming languages are supposed to give the reader two very valuable things: knowledge of when to use the language and knowledge of when not to use the language. Programming Clojure is lacking in the latter--this is not a unique problem as most books about a language really sell their language. All too often in my professional career I see a solution and think, "Wow, that really was not the right tool for the job." (I'm looking at you, Java) Clojure definitely has its strengths and weaknesses despite very little evidence of the latter in this book although I was directed to a QCon presentation where the author speaks more about where Clojure excels in real life.

That said, the book is a great fit for the object oriented Java developer who does not also code a lisp-like language regularly. I say that because Chapter Two deals with reviewing all of the facets of Clojure--most of which are found in other Lisp languages which might be seen as remedial to a proficient Lisp developer. However, before you skip it entirely, there are important notes that Halloway injects into these chapters ranging from how not to do things in Clojure to the minute differences and implications they hold. Chapter Five dives into the fundamentals and features of functional programming in Clojure. This chapter was especially useful to me as I'm not used to languages featuring things like lazy sequences, caching of results or tail-call optimization. Working through the examples in Chapter Five really opened my eyes to some of the more powerful aspects of FP. Like how an infinite sequence can easily be handled by Clojure and its laziness allows you to only pay for what you need from that sequence. While definitions of infinite sequences are also possible in Haskell or Python, Clojure brings this capability to the JVM (not that anything is preventing a more verbose Java library from handling such structures).

Chapter Three focuses a lot on Clojure's interaction with Java and does a great job of showing you how to rewrite part of your Java project into Clojure and run it on the JVM. This includes calling Java from Clojure, creating and compiling Clojure into java classes, handling Java exceptions in Clojure and ends with the beginning work in Lancet (the build tool the book strives to create using what we learn in each chapter). It also contains a bit on optimizing your performance when working with Java in Clojure. This theme continues through the book as Halloway knows that one of Clojure's main selling points is that it can be so much faster than Java if you're willing to put in the extra work and planning to utilize pure functional programming.

In Java, everything is an object. In Scheme, everything is a list. Well in Clojure, the main staple is sequences which brings us to Chapter Four: Unifying Data with Sequences. While this chapter succeeds in teaching how to load data into sequences, how to consume data from sequences and how to force evaluation of lazy sequences, it felt like one of the weakest chapters in the book. This is all necessary in learning Clojure but Halloway skimps on examples and could stand to add some more examples on what is and isn't seq-able, seq-ing on various things and performing functions on various things.

Multicore chips are all the rage these days. And right now it seems that developers are by and large content with coding single threaded applications. But that may change in the future when the user expects more than a few cores in usage. In the introduction, Halloway argues a few reasons why we all should use Clojure and one of those reasons happens to be the somewhat sound logic that we will all have cores coming out of our ears in the near future. That means that as a developer you have the option to spawn more threads which means coordination of threads which means you will be forced to do the dirty dance of concurrency. Chapter Six is entirely devoted to this and, honestly, I reread a lot of this chapter as there are several update mechanisms and models that you can use to manage concurrency in Clojure. Unsurprisingly there is no silver bullet for concurrency even in Clojure. This book has but a handful of figures and their formatting leaves much to be desired but the two in this chapter are necessary references for deciding if you should use refs and software transactional memory, atoms, agents, vars or classic Java locks. This is a potent chapter that ends with a snake game implementation in Clojure demonstrating some basic concurrency. While Clojure protects you from some classically complex issues and may make concurrency vastly more succinct, it still requires a lot of thought and planning. Halloway provides good direction but clearly hands on experience is a necessity in this realm.

Chapter Seven focuses entirely on macros and is somewhat disheartening in that it presents an extremely powerful feature of Clojure that is also very complex. Halloway gives two rules and an exception for Macro Club. The first rule is: "Don't Write Macros." The second rule is: "Write Macros if That Is the Only Way to Encapsulate a Pattern." The exception is you can also write macros if it makes calling your code easier. Halloway does a good job of explaining the basics of macros in Clojure and breaks them down via a taxonomy into categories and examples of macros in Clojure. Macros are a necessity when you're trying to augment Clojure by adding features to it or if you are creating a Domain-Specific Language (DSL). Macros in Clojure do seem easier than macros in most other Lisp languages. At the end of Chapter Seven, you create a basic DSL for Lancet which was helpful even though I was left feeling helpless in the face of macros. Despite the complexity of macros in Chapter Seven, Eight's multimethods are similar to Java polymorphism and was much easier to wrap my head around than macros. Multimethods are used very infrequently (seven times in the five thousand lines that compose the Clojure core).

Chapter Nine is unfortunately less than twenty pages and deals with "Clojure in the Wild." You would think that a book in the series of Pragmatic Programmer would have more pragmatism than the features of a language with Lancet but let's face it--Clojure is a relatively young language. Nine covers automated tests, data access and web development. The automated testing is a short section on Clojure's test-is packaging. The database stuff appears to be little more than wrappers around the already mature JDBC. The web development consists of an intro to Compojure which is similar to and Sinatra. Compojure shows a lot of promise in reducing the amount of code one needs to write a basic web application. It lacks the feature set and support that Rails has with rapidly building CRUD applications but holds a lot of potential to be flushed out into something similarly powerful. Halloway says his introductions to these projects should "whet your appetite for the exciting world of Clojure development" but I think a more accurate description is that these brief brushes with functional projects leaves the reader ravenously blinded by hunger for more.

Some final thoughts on the book: I caught only two very minor typos in the book. It's all English and code. There were no pictures or illustrations in this book except for one on page 96 in which a tiny drawing appears named Joe who asks a question about vectors. Oddly enough, I didn't find Joe on any of the other three hundred pages. It was very easy to work through this book from cover to cover and the example code was very instrumental in my understanding of Clojure. As a Java monkey, rereading sections seemed a requirement although the book is concise enough for me to enjoy in my free time over one week. Halloway cites mostly websites and utilizes tinyurl to reference blogs like Steve Yegge's blog and frequently he references Wikipedia. Only three of his many citations are other printed books (although one of them is Gödel, Escher, Bach: An Eternal Golden Braid). Halloway's greatest strength is the engaging examples (like the Hofstadter Sequence) that he picks and provides to the user and I hope that future editions of the book build on this as well as expand on the growing base of Clojure projects out there. His github is rife with both instructive and pragmatic examples that could stand to be included in a future book.

Some final thoughts on the language: Clojure holds a lot of potential that is yet to be realized. I cannot say yet whether the succinct syntax offers a good balance between quick coding and readability. To the uninitiated, the code can look like a jumble of symbols. Yes, we escape the verbosity of Java and the kingdom of nouns but is what Clojure offers (a neighboring kingdom of verbs) better? While Clojure is concise, it requires a lot of keywords which required a lot of usage look up when starting. Clojure code is potent and powerful. A mere five thousand lines of Clojure code create your engine--the core of the language. I assume this brevity is due to ingenious reuse that Clojure can offer but I would hate to be the person to maintain that code if I was not the author. What's better is that this code is quickly conjured at the REPL if you wish to read it yourself or augment a feature. A sage coworker who has seen much more than I in this business of software development recommended Clojure to me. He was right that it is a very interesting and innovative language but in my opinion it has a long way to go before it becomes the next Ruby or Java. Clojure needs an equivalent to Ruby on Rails and it's fighting an uphill battle against all the developers like myself that left college with so much object oriented coding and so little functional programming (although Scheme is my alma mater's weed out course). If you find yourself stagnating and are thirsty for some continuing education in the form of a stimulating challenge, I recommend Clojure (and this book on Clojure). Hopefully Clojure's full potential is realized by the community and it finds its deserved place in many developer's tool sets as the right tool for some jobs.

You can find Programming Clojure in three DRM-free formats and hard copy from the publisher's site. For a sample of the author's writing and to get a feel for how he injects Clojure code into it, check out his blogs on his company's website.

You can purchase Programming Clojure from Slashdot welcomes readers' book reviews -- to see your own review here, read the book review guidelines, then visit the submission page.


This discussion has been archived. No new comments can be posted.

Programming Clojure

Comments Filter:
  • on LISP (Score:1, Insightful)

    by Anonymous Coward

    Any sufficiently complex programming language will, over time, expand its features until it reinvents LISP.

  • Some Guidance (Score:1, Flamebait)

    by ari_j ( 90255 )
    As you read through the many comments that will be posted on this article, please keep the following advice in mind: People who spell it LISP are not qualified to judge modern (read: post-1984) Lisp-family languages, probably having 100% of their exposure come from a one-hour lecture and small homework assignment in a programming languages course taught by someone who thought that Common Lisp is a single-paradigm functional language.
  • "Programming Clojure" is a great book. Any book on a new programming language is going to evangelize the language - it's up to the reader to keep an open mind and decide for herself that it's a good fit for what they do. The book is started to get a bit dated now with Clojure up to release 1.2 now, but is still worth getting in eBook form to get a good understanding of the language fundamentals. I would also recommend the "Joy of Clojure" which is in early-access from Manning. They take a different approach
  • in
  • In all seriousness there are two concepts are are especially onerous and difficult to do well in computer science:

    1.) Asynchronous code (threads).
    2.) Self modifying code (functional programming).

    Both concepts are difficult for typical developers to get right. What the world needs is a revolutionary approach to these two topics such that average developers have the necessary tools to produce quality code for multi-core machines.

    Someone needs to take design patterns to the concrete level and create design pat

    • by stuart.halloway ( 1813346 ) on Monday May 17, 2010 @03:12PM (#32242332) Homepage
      "Functional programming" and "self-modifying" code are closer to opposites than synonyms. Functional programs work with immutable data, eliminating the confusion that ensues when things can change behind your back. Clojure makes functional programming efficient (via persistent data structures) and accessible to mainstream developers (on the JVM). Also, Clojure connects FP to an understandable model for state and time (see []). People have been taking design patterns to a concrete level for years now, it's called copy/paste reuse. Lisp has had the design patterns problem solved for decades, and Clojure is no exception.
    • by Nadaka ( 224565 ) on Monday May 17, 2010 @04:04PM (#32243346)

      2: functional programming and self modifying code have nothing to do with one another. functional programming transforms a set of inputs into a set of outputs without reference to any external state. It is a purely mathematical expression. Functional programming languages can be used to write self modifying code, but so can can most languages.

      1: if you understand what you are doing, asynchronous programming is easy. All you have to do is prevent screwing up the shared state between threads. Since functional languages have no state to share, you can avoid 99% of the pitfalls of dealing with threading.

      I deal with threading every single day. Problems occur when the problem is intractable (and then nothing can help it split), the solution is poorly designed (to much shared state), the language is poorly documented/designed (ambiguous thread safety, inability to be thread safe) or the developer just does not know what they are doing.

      Functional programming is good at threading because it eliminates shared state completely. The question is no longer "can I split this, what do I have to rewrite from scratch, and will it be worth it?", but rather "will splitting this be worth the overhead of creating, context switching and synchronizing the threads?"

      Abstracting multi-threading to make the syntax easier for inexperience developers is a mistake unless you can also fundamentally prevent most of the issues that make multi-threading a pain in the ass as well. Adding real closures to more traditional languages like c++, java, c# etc would go a long way towards making multi-threading easier to deal with because the vast majority of problematic code in relation to multi-threading will produce a compile error if used within a real closure.

      • by Mybrid ( 410232 ) on Monday May 17, 2010 @04:34PM (#32244000)

        2: functional programming and self modifying code have nothing to do with one another.

        This is the equivalent of saying lamda functions have nothing to do with functional programming.

        1: if you understand what you are doing, asynchronous programming is easy. All you have to do is prevent screwing up the shared state between threads. Since functional languages have no state to share, you can avoid 99% of the pitfalls of dealing with threading.

        Therein lies the rub and I'm glad you put it out there. Take any class of computer science students you wish and test them on asynchronous programming and synchronous programming. Guess which one will have the lower scores? Not for all people though, and presumably you are one of those for which asynchronous programming comes natural and easy too. Therein lies the rub. This concept also applies to functional programming vs imperative and came up in a code review I had recently for some Perl code. The reviewer asked my why I used "for loops" as opposed to "map". I never use map. I said because it has been proven time-and-time again that people do not understand "map" as well as they do "for loops", especially the side effects in Perl (not truly functional). There is no performance difference either way, but there is a human difference. I argue my code is more maintainable at the expense of a few lines of ASCII text. You have no idea if the person following you finds what you find to be understandable equally understandable and in the case of "map" using a for loop is trivial; the cost to me is nothing and the opportunity for maintainability greater.

        • > Not for all people though, and presumably you are one of those for which asynchronous programming comes natural and easy too. Therein lies the rub.

          It's really easier to write multithreading code that works "most of the time" (than the ST counterpart.) What's difficult is to discover and avoid the potential race conditions; most people just assume it's ok if their 30 functional unit tests pass clean.

        • This is the equivalent of saying lamda functions have nothing to do with functional programming.

          No it's not. The whole point of functional programming is to maintain referential transparency. It is literally impossible to modify a named value, including functions.

          Self-modifying code is what you get when you make your imperative, referentially opaque code modify itself. You CANNOT do that in a pure language. Although purity isn't the gold standard in functional programming, it is a goal functional progra

        • Re: (Score:3, Insightful)

          by arevos ( 659374 )

          2: functional programming and self modifying code have nothing to do with one another.

          This is the equivalent of saying lamda functions have nothing to do with functional programming.

          No, it's equivalent to saying that lambda functions have nothing to do with self-modifying code.

          I'm not sure where this confusion of yours has arisen. Why would you think an anonymous function is self-modifying code?

        • Re: (Score:2, Insightful)

          by drewhk ( 1744562 )

          Asynchronous programming sucks big time (at least for programs that are beyond trivial). There is a reason for the idea of "blocking calls" -- it makes code much more readable. Although the asynchronous model leads to faster code.

          I earn part of my money by doing Discrete-Event Simulations, which are by their nature much like Actors - everything is done through message passing, and asynchronously (i.e - no blocking calls).

          It always make me twitch when people come and try to sell abstractions like STM or Acto

      • I deal with threading every single day...the language is poorly documented/designed (ambiguous thread safety, inability to be thread safe)

        I also deal with threading every single day, and in C++, which is a poorly designed language as you describe it. But I don't have any problems with it, due to the following simple conventions:

        1. synchronization primitives must always be locked in the same order and unlocked in the exact reverse order.
        2. races between threads are solved by sending results in another thread.
    • If metaprogramming is self-modifying code, then every compiled program is self-modifying: there is no fundamental difference between translating a program to assembly and translating it to the same language in which it was written; if macros were inherently self-modifying, MACROEXPAND would be useless. If closures were self-modifying, then every C callback system that incorporated an opaque user-data parameter would also consist of self-modifying code.

      In all my years*, I have seen no poster demonstrating a

  • I am concerned about using this for large software engineering problems about having data and methods scattered loosely about the code because Clojure does not enforce encapsulation. That brings up the old joke about "Chinese programming in LISP": an hour later you will have forgotten what you coded. More recent versions of LISP such as Scheme implement OOP more soundly that the version in Clojure.

    Soem of the nice thing in Clojure like the rich set of set-notation are not LISP-specific. There is talk of
    • Re: (Score:3, Informative)

      I am not sure if you know what you are concerned about, as you use the word 'abstraction' in the title of your post, the word 'encapsulation' in the first sentence, and then conclude with 'OOP'. So I will take them one at a time. (1) Clojure is built around abstractions, there is a rich set of carefully designed interfaces. (2) Clojure's approach to data is certainly not what an OO person would expect. In Clojure, *data* encapsulation is considered folly. However, this must be taken in the context of severa
      • Clojure's approach to data is certainly not what an OO person would expect. In Clojure, *data* encapsulation is considered folly.

        Wait, what? How can data encapsulation be "considered folly", in a language which is quite deliberately called "Clojure" - when closures are, perhaps, the most succinct way to tightly encapsulate data?

        Just close over some scoped variables, and return the closure from that scope [] - and there's your encapsulation!

        In fact, this technique is widely practiced in JavaScript today for exact same purpose.

        • A good question. In Clojure, the idiomatic way to do the good part of OO is to use defrecord. Records created in this way do not encapsulate data. The data is immutable, and is directly accessible via public fields or map keyword lookup. Some other interesting differences from most OO: You *must* program to protocols (interfaces)--records cannot implement methods that are not part of a protocol. And implementation inheritance is forbidden. Defrecord supports a very iterative approach to development. You c
    • In my experience with functional languages, the trouble isn't remembering what you coded, but where you put it. Reading functional programming is really easy. You barely even have to understand the operators, as long as you understand that a (mathematical) function is a certain type of relation (a many-to-one relation). Equivalently, a function is the same as a set of ordered pairs with the property that if (x,y) and (x', y') are in the set, and x = x', then y = y'. Equivalently a (computable) function

  • by Bob Hearn ( 61879 ) on Monday May 17, 2010 @03:54PM (#32243160) Homepage

    Exactly. That was the big problem I had with the book: it's written for Java programmers. I am intrigued by the language, but I would much prefer a book that treats the language on its own terms.

    • Re: (Score:3, Informative)

      Bob, I suspect you would like [], available in early-access electronic form. The authors know Clojure well and tell a great story.
    • by ascari ( 1400977 )

      My biggest problem with the book was that it doesn't explain what exactly the problem is that clojure solves. (Or attempts to solve.)

      Why shouldn't I stick with one of the already existing functional languages for functional programming? Why shouldn't I stick with Java (or any of the other JVM-languages) if I need access to the reams of Java classes that already exist? Why not use Erlang if you're thread-paranoid? And so on. Nothing wrong with a new shiny language, but please explain why it is better than t

  • []

    Concepts A number of concepts and paradigms are specific to functional programming, and generally foreign to imperative programming (including object oriented programming). However, programming languages are often hybrids of several programming paradigms so programmers using "mostly imperative" languages may have utilized some of these concepts.[24] Comparison of functional and imperative programming. Functional programming is very different from imperat

Today is a good day for information-gathering. Read someone else's mail file.