Clojure is a better Java than Java

Eric Normand's Newsletter
Software design, functional programming, and software engineering practices
Over 5,000 subscribers

A few weeks after starting with Clojure, I was talking to my friend John about how cool it was. John is one of those super practical "use the best tool for the job" types of engineers. I was talking excitedly and something just slipped out: "Clojure is a better Java than Java."

As soon as I said it, I knew it was the end of the conversation. It did not go well with him. Until then, the discussion had been basically which tool was better for the job, Clojure versus Java. But then I broke that completely and said that Clojure was better at Java's job than Java. This he could not take.

How is it possible that Clojure is better than Java at its own game? Bear with me. I have the time I didn't have with him to spell out everything. At the end, you can decide for yourself.

The basic operations of Java

One of Clojure's stated goals is to give you easy access to the underlying host platform. In practical terms, it means you can construct Java objects and call methods on them. But this is mostly what we do in Java---what I had been doing professionaly for years. I just want to show that Clojure can do what Java does, then we'll get into why it's better.

Construction in Java is done with the new keyword. You type new Object() and you have a new Object. Clojure lets you do that very easily using the new special form or more succinctly with a "dot syntax".

(new Object)

or

(Object.)

Both do the same thing.

Method calls in Java are done with a dot syntax:

o.push(5);

Method calls in Clojure are done with the "dot prefix syntax".

(.push o 5)

You have the same prefix syntax as Clojure functions, but the dot means it's a method call.

Like I said, Clojure is not better just in this, only on par with Java. So in what ways is Clojure better?

The Java types

The Java type system is a static checker for the types of variables and arguments. It's supposed to catch bugs and make your code more robust. But is this really true? People complain all the time about how verbose the Java type system is. I mean, what about this kind of code:

List customers = new ArrayList();

Do I really have to say it twice? I make a list, what else could it be? And does it really make it safer that you had to say it twice? A lot of people (myself included) argue that the Java type system gets in the way more than it helps.

Clojure eliminates 99% of these type annotations. You give up the small amount of safety for a huge amount of flexibility.

Namespaces instead of classes

What do you do when you want to organize some Java code? You make a new class. You move the code in there, maybe as static methods, maybe regular methods. Then you get to call them from elsewhere.

But when you make a class, you're also making a new type. A new type just to organize some code? How do you know the class is meant to be instantiated instead of used for its code? Clojure gives you namespaces which are just for code organization and avoiding naming conflicts. Clojure makes it much clearer what the intent is.

Functions instead of classes

Another thing you see in Java is classes that have one method. Maybe it's an instance of Runnable or maybe it's an instance of a Thread.UncaughtExceptionHandler. You're supposed to make a new class (maybe an anonymous class) that implements these interfaces. Then you instantiate them when you need them.

It has been said before that if you've got a class with one method, it's probably just a function. Clojure gives you functions for just this purpose. Again, Clojure is clearer with the intent, even though it doesn't have static types.

Chaining

One thing that people really like about calling methods in Java is that they can chain. You can say:

a.b().c().d();

It feels nice and convenient. And it usually is. But it has some downsides.

The object that .b() returns has to have a .c() method. Basically, you're only able to chain as long as all of the classes have been designed to do so. And many times, you're using someone else's library and you can't modify classes. The standard answer is to wrap everything in a Decorator. You add a new class to your code for every class you need to wrap. Ugh!

The relevant chaining macro is called "thread first" in Clojure:

(-> a
  .b
  .c
  .d)

There's no savings there. But I can add calls to my own functions right in there, without relying on the designer of those classes to have thought of what I wanted to do.

(-> a                   ;; take object a
  .b                    ;; call method .b
  (call-my-function 10) ;; take the return and pass it to call-my-function
  first                 ;; take the first of whatever that returns
  str)                  ;; convert what that returns into a string

It's the best of both worlds. Things read top-to-bottom like a list of steps and you can mix methods and functions.

Object configuation

Clojure has the doto macro. It directly replaces the typical Java "configuration" pattern. You create an object then set a bunch of properties on it.

Thread t = new Thread(runnable);
t.setName("My Thread");
t.setPriority(100);
t.setUncaughtExceptionHandler(handler);
t.start();

In Clojure, this becomes:

(doto (Thread. runnable)
  (.setName "My Thread")
  (.setPriority 100)
  (.setUncaughtExceptionHandler handler)
  (.start))

If I do want to give it a name, I just have to do this:

(let [t (doto (Thread. runnable)
          (.setName "My Thread")
          (.setPriority 100)
          (.setUncaughtExceptionHandler handler)
          (.start))]
  ...)

Seqs instead of iterators

Java Iterators are great. They give you uniform access to a lot of different collections. But they're not perfect. They're stateful and they don't work with everything (String and arrays, plus custom collection types---why do Java libraries define their custom iterator types?). Clojure's Seqs, on the other hand, are totally compatible with Iterators, they're not stateful, and they work with most everything (Strings, arrays, nil, and collections).

Nil punning

One of the worst mistakes Java made was to include null as a value. Any reference can be null. Just try to call a method on null and you get a NullPointerException.

Nil punning is a common feature of Lisps. Typically, it means that nil represents the empty list and boolean false. Because typically you're not calling methods in Clojure, you're calling functions, the problem of Null Pointers almost goes away. Clojure's nil is actually an expected argument in so many functions, it just seems to fall through more gracefully.

Just as a simple example to make my point, this is considered dangerous in Java:

if(myString.equals("Hello")) {
....

Why? Because what if myString is null? Uh oh! You're supposed to write:

if("Hello".equals(myString)) {
....

Clojure handles this just fine:

(if (= my-string "Hello")
  ...

Conclusions

I don't mean to bash on Java. I just wanted to make that small point that for the stuff Java is mostly made of, Clojure kind of smoothes over a lot of the difficult parts. It's not perfect, either. But I think it makes the Java parts of Clojure kind of pleasant.

You still have to understand how methods work and what classes you have in the standard library and all of that. If you are new to the JVM, you'll want to learn those things. You can buy the JVM Fundamentals for Clojure course, which goes over interop (object construction and method calls) as well as the more useful parts of the Java standard library. You can also get it (along with 37 hours of videos) as part of a membership to PurelyFunctional.tv.

Sean Allen
Sean Allen
Your friendly reminder that if you aren't reading Eric's newsletter, you are missing out…
👍 ❤️
Nicolas Hery
Nicolas Hery
Lots of great content in the latest newsletter! Really glad I subscribed. Thanks, Eric, for your work.
👍 ❤️
Mathieu Gagnon
Mathieu Gagnon
Eric's newsletter is so simply great. Love it!
👍 ❤️