Jay Fields is a software developer at DRW Trading. He has a passion for discovering and maturing innovative solutions. His most recent work has been in the Domain Specific Language space where he's delivered applications that empowered subject matter experts to write the business rules of the applications. He is also very interested in maturing software design through software testing. Jay is a DZone MVB and is not an employee of DZone and has posted 116 posts at DZone. You can read more from them at their website. View Full User Profile

Readable Clojure Without a Java Equivalent?

03.25.2011
| 5486 views |
  • submit to reddit

I've recently joined a new team and we've been doing a bit of Clojure. If you've never done Lisp (and I hadn't before I found Clojure) it's natural to ask the quesetion: Will programming in Lisp, especially Prefix Notation, ever feel natural? The question came up a few weeks ago and I had two answers

First of all, I've never been really upset about parenthesis. In fact, I've been doing a bit of Java these days and I don't see much difference between verify(foo).bar(eq(100), any(Baz.class), eq("Cat")) and (-> foo verify (.bar (eq 100) (any Baz) (eq "Cat))). By my count it's the same number of parenthesis. Where they're located moves around a bit, but I don't consider that to be a good or a bad thing.

People also like to bring up examples like this:

(apply merge-with +
(pmap count-lines
(partition-all *batch-size*
(line-seq (reader filename)))))
Stefan Tilkov addressed this in a previous blog entry, and (in the comments of Stefan's entry) Rich Hickey points out that you can use a few different versions if you prefer to lay your code out in a different manner. Below are two alternative solutions Rich provided.

; The same code in a pipelined Clojure style:

(->> (line-seq (reader filename))
(partition-all *batch-size*)
(pmap count-lines)
(apply merge-with +))

; and in a step-wise Clojure style with labeled interim results (a la Adrian’s comment):

(let [lines (line-seq (reader filename))
processed-data (pmap count-lines
(partition-all *batch-size* lines))]
(apply merge-with + processed-data))
I've felt the same way for awhile. You can write Cobol in any language. The real question is, are you up for learning how to solve problems elegantly and idiomatically in a new language. If you're willing to invest the time, you'll be able to find out for yourself if it feels natural to write idiomatic Clojure code.

That was my answer, until today. While working on some Java code I stumbled on the following Java method.
public void onFill(int fillQty) {
this.qty = Math.max(this.qty - fillQty, 0);
}
This is a simple Java method that is decrementing the outstanding quantity state of an order by the amount of the order that just been filled. While reading the line I couldn't help but feel like there should be a more elegant way to express the logic. You want to set the outstanding quantity state to the current outstanding quantity minus what's just been filled, but you also never want the outstanding quantity to go below zero. I read from left to right, and I really wanted a way to express this logic in a way that followed the left to right pattern.

In Clojure, this is easy to do:
(swap! qty #(-> % (- fill-qty) (Math/max 0)))
For readers who are less familiar with Clojure dispatch reader macro, the above example can also be written as:
(swap! qty (fn [current-qty] (-> current-qty (- fill-qty) (Math/max 0))))
In the example above swap! is setting the qty state with the return value of the function.

If you're really new to Clojure, that might still be too much to take, so we can reduce the example and remove the state setting. Here's the version in Java that ignores setting state.
Math.max(this.qty - fillQty, 0);
The example below is a logical equivalent in Clojure.
(-> qty (- fill-qty) (Math/max 0))
When reading the above Java example I'm forced to put Math.max on my mental stack, evaluate this.qty - fillQty, and then mentally evaluate the method I put on the stack with my new result and the additional args. This isn't rocket science, but it's also not how I naturally read (left to right). On the other hand, when I read the Clojure version I think - take the current quantity, subtract the fill quantity, then take the max of that and zero. The code reads in small, logical chucks that are easy for me to digest.

Obviously, the Java code can also be rewritten in a few other ways. Here's an example of Java code that reads left to right and top to bottom.
public void onFill(int fillQty) {
this.qty -= fillQty
this.qty = Math.max(this.qty, 0);
}
And, I can do something similar in Clojure, if I want.
(swap! qty #(- % fill-qty))
(swap! qty #(Math/max % 0))
While it's possible to write Clojure similar to the above example, it's much more likely that you'd use a let statement if you wanted to break up the two operations.
(defn update-qty [current fill-qty]
(let [total-qty (- current fill-qty)]
(Math/max total-qty 0)))

(swap! qty update-qty fill-qty)
The above example is probably about equivalent to the following Java snippet.
public void onFill(int fillQty) {
int newTotalQty = this.qty - fillQty
this.qty = Math.max(newTotalQty, 0);
}
So, I can write code that is similar to my options in Java, but I'm still left wanting a Java version that is similar to this Clojure example:
(swap! qty #(-> % (- fill-qty) (Math/max 0)))
The only thing that springs to mind is some type of fluent interface that allows me to say this.qty = this.qty.minus(fillQty).maxOfIdentityOrZero(), but I can't think of a realistic way to create that API without quite a bit of infrastructure code (including my own Integer class).

(note, you could extend Integer in a language with open classes, but that's outside the scope of this discussion)

The last Clojure example is definitely the version of the code I would prefer. My preference is based on the way the code reads in concise, logical chunks from left to write. I don't have to solve inside out like the original java version forces me to, and I don't have to split my work across two lines.

I'm sure there are situations where Java allowed me to create an elegant solution that wouldn't have been possible in Clojure. This entry isn't designed to send a "Clojure is better than Java" message. I don't believe that. However, before today I've held the opinion that you can write Clojure that logically breaks up problems in ways very similar to what you do using Java. However, I've now also expanded my opinion to include the fact that in certain situations Clojure can also allow me to solve problems in a way that I find superior to my options within Java.

And, yes, after a bit of time getting used to Lisp syntax, it definitely does feel perfectly natural to me when I'm developing using Clojure.

 

From http://blog.jayfields.com/2011/03/readable-clojure-without-java.html

Published at DZone with permission of Jay Fields, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Fab Mars replied on Fri, 2011/03/25 - 7:44am

Since when isn't Math.max(var, 0) an elegant way to minor by zero? And this is rhethorical anyway.

Esko Luontola replied on Fri, 2011/03/25 - 11:03am

Let's raise the level of abstraction and use an intention-revealing name.

    public void onFill(int fillQty) {
        this.qty = nonNegative(this.qty - fillQty);
    }

    private static int nonNegative(int i) {
        return Math.max(i, 0);
    }

I might also make qty a domain object and give it a well named method for reducing the quantity.

Andy Leung replied on Fri, 2011/03/25 - 11:31am

Interesting article. I think it's interesting because author is trying to show that Clojure is somehow more elegant (not better) than Java by showing how Java code looks; in contract, it actually gave me feeling that Java code looks more elegant. Well it's my personal opinion but I really feel sick of typing 7 symbols on one line of code doing the same thing that Java requires only 5 or less symbols. Example:

Clojure:(swap! qty #(-> % (- fill-qty) (Math/max 0)))
Requires: (!#(->%(--)(/)))

Java: public void onFill(int fillQty) {
this.qty = Math.max(this.qty - fillQty, 0);
}
Requires: (){.=.(.-,);}


So comparing symbols itself, Clojure is 16 and Java is 13. 3 symbols may not be much because Java has more character count but I can type 10 letters with the same amount of time of typing 3 symbols. So I still like Java syntax more. :P

Bayan Khalili replied on Sat, 2011/03/26 - 1:35pm

@Andy Leung

The Clojure code is doing much more than the Java code. At the cost of a few extra symbols, it allows the state to be set atomically, even within a multithreaded environment. (see here)

The author was trying to show how each language calculates the value. But the way Clojure handles state would be unfamiliar to people who are new to the language, and it would detract from the main focus of the article. This is why he pulls out the state setting code further into the article.

Tero Kadenius replied on Sat, 2011/03/26 - 6:46pm

@Andy Leung:

I've never heard anyone use such an innovative argument for defending Java's excessive boiler plate and criticizing Clojure's expressiveness at the same time.  Applause! ;)

On a more serious note, I've just recently grasped the beauty of the threading macros. It's nice how Clojure delivers fluent-interface-like functionality out-of-the-box, whereas in Java you would have to implement all sorts of builder patterns and domain objects and subclass this and extend that in order to achieve the same thing. You don't have to maintain the code you don't write.

Even though this isn't a competition, Clojure wins hands down. ;)

Andy Leung replied on Fri, 2011/04/08 - 9:53am in response to: Tero Kadenius

LOL...but I really have problem reading such code doing "fluent-interface-like functionality out-of-the-box" kind of code because I thought readability is just one of the fundamental requirements of a programming language and the way developers write them:
(import '(java.util.concurrent Executors)) (defn test-stm [nitems nthreads niters] (let [refs (map ref (replicate nitems 0)) pool (Executors/newFixedThreadPool nthreads) tasks (map (fn [t] (fn [] (dotimes [n niters] (dosync (doseq [r refs] (alter r + 1 t)))))) (range nthreads))] (doseq [future (.invokeAll pool tasks)] (.get future)) (.shutdown pool) (map deref refs)))

And this is a simply do nothing multi-thread example from Clojure web site, but if you have complex logic, are you going to put everything in this block? or you still wrap your logic in object-kind-of-context (block) before submitting all tasks??? So what's exactly different than Java's Concurrency package and I can imagine syntax looks very similar. PLUS you mentioned with Java I would have to implement all sorts of builder patterns? So are builder patterns bad? I see the Clojure code above as builder pattern too. Please enlighten me. :)

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.