Clojure for a Java developer

Becoming a functional programming nerdola was never so easy.

Fabrício Yamamoto
WAES
7 min readMar 22, 2024

--

A quote from the author that says: “How a Java Developer can embrace a whole different programming paradigm with Clojure, a functional JVM language”

I have been a passionate Java developer for a long time, but people don't know that my first professional programming language was AutoLISP, a dialect of LISP built for AutoCAD. It was like that for some years until I finally started my career in tech, but I always felt that something was missing.

In the first years of my tech career, I worked for a huge bank in Brazil. During that time, a new fintech was rising (Nubank); it was a new all-digital, feeless credit card company that happened to build its systems using Clojure, which was, until then, a language known only by the true nerdolas of functional programming.

While we struggled to migrate everything we had to Java 8, this new company was growing and changing fast, threatening all the big banks. Time passes by, and now they happen to own the language.

In an illustrative scene, a figure with the minimalist, stylized Clojure logo as its head — depicting a black and white “C” wrapping around a smaller “l” — assertively punches another figure, whose head is replaced by the Java logo, symbolizing a coffee cup with steam. This dynamic image metaphorically showcases Clojure’s dominance over Java, captured in the moment of impact, emphasizing the contrast and competition between the two programming languages.

First contact and giving up

That's how I first got in touch with Clojure, a LISP dialect built for the JVM that focuses mainly on functional programming and has Java interoperability. I even tried to start coding something with it, but I was not very successful.

Back then, finding sources to learn how to code properly was difficult. Most people I knew who coded in Clojure were taught in Nubank, and kind of hated it. So, I was one of the few maniacs who wanted to know more about it just for fun (which didn't change much). Because setting up the environment to start coding was tough, I did some simple exercises and gave up.

The comeback

I could not give up so easily, so I observed the evolution of the language from far away. I would randomly search for Clojure in Google and check if some good tools were released, such as books, articles, videos, code snippets, etc. Until I finally found the Cursive plugin for IntelliJ IDEA. This was a game changer, and I will tell you why.

Familiar IDE for Java developers

The lack of an easygoing IDE hits us hard. I have a book that proposes learning how to use Emacs first so you can start learning Clojure; it has a whole chapter on how to set it up and running. But, unlike most LISP programmers, we already had IntelliJ, Eclipse, or even NetBeans to code. This initial learning curve was tough. So, the idea of coding Clojure inside IntelliJ is pleasant.

Easy installation

Today, installing is easier; you have straightforward ways to install for Windows, MacOS, Linux, and Posix. Back then, you had to download and execute the Clojure jar manually.

Decent Package Manager

They have Leiningen, their package manager, where you can create a project or add dependencies to your current one, just like our beloved Maven.

Uncle Bob loves it

At the start of my career, I got in touch with the "Clean" literature and enjoyed it. Coincidently, after some time, I heard about Clojure; he wrote this article telling why Clojure is his favorite language and why it is a strong candidate for being the language that he believes will eventually become the standard language that all programmers use.

Uncle Bob spotted as an absolute Clojure enjoyer

What is LISP? What is Clojure?

You may have already heard that silly joke about Lost In Stupid Parentheses, but the actual meaning comes from the word list, as LISP is a LISt Processor. The name fits everything perfectly; one nice and important thing about LISP is that code is data, and data is code.

LISP uses s-expressions based on lists, symbols, and data. This means (* 2 (+ 3 4)) is a valid LISP expression, and the compiler would interpret it as a tree like this:

Tree data structure representing the S-expression (* 2 (+ 3 4))

And this is everything about LISP syntax. The beauty of this structure when writing code is that now you and the compiler speak the same language; it is called homoiconicity.

Clojure nowadays is one of the most used LISP dialects, as Common LISP and Scheme. Below, we can see an example of its homoiconicity.

;; this is the definition of a main method that
;; should print a random int from 0 to 9
(defn -main [] (println (rand-int 10)))
=> #'clj4java.core/-main
;; now we can transform this into data using quote and afterwards
;; save this definition inside the reference "code"
(def code '(defn -main [] (println (rand-int 10))))
=> #'clj4java.core/code
;; then we can eval it to retrieve the reference
(eval code)
=> #'clj4java.core/-main
;; and after we have the reference we can execute it
((eval code))
5
=> nil

Getting our Hands Dirty

Returning to our beloved Java, here is a small table that tries to correlate both languages.

As we can see, while Java uses more syntactic shenanigans such as commas, keywords, curly braces, and unary operators with their own rules for compiling evaluation, Clojure uses only lists with symbols and data; the first item of each list is a call to a function, and the remainings in the list are the parameters passed to this function. Also, all the function implementations can be found inside the docs.

Clojure Collections

Of course, Clojure is not made entirely of lists. It also has some collections that help the developer; they could be sequential, such as vectors and lists, but also hashed as maps and sets.

And because Clojure does not have something such as classes or objects, it leverages a lot on collections, which gives us some nice utilities like:

(drop 2 [1 2 3 4 5])
=> (3 4 5)
(take 9 (cycle [1 2 3 4]))
=> (1 2 3 4 1 2 3 4 1)
(interleave [:a :b :c :d :e] [12 3 4 5])
=> (:a 12 :b 3 :c 4 :d 5)
(partition 3 [1 2 3 4 5 6 7 8 9])
=> ((1 2 3) (4 5 6) (7 8 9))
(map vector [:a :b :c :d :e] [1 2 3 4 5])
=> ([:a 1] [:b 2] [:c 3] [:d 4] [:e 5])
(apply str (interpose \, "asdf"))
=> "a,s,d,f"
(reduce + (range 100))
=> 4950

If there are a lot of parentheses, you can also use Rainbow Brackets so you do not get lost.

Java Interoperability

Clojure offers good interoperability with Java. There are some helpers to deal with constructors, calling methods from an object, and even calling methods of an object retrieved from another method.

;; Macros for constructors
(new Date)
=> #inst"2024-03-05T18:49:10.318-00:00"
(Date.)
=> #inst"2024-03-05T18:49:11.344-00:00"

;; Macros for calling an method of a Java object
(let [d (Date.)] (. d getTime))
=> 1709664552280
(let [d (Date.)] (.getTime d))
=> 1709664553037

;; Macros for calling static methods
(. Math PI)
=> 3.141592653589793
(Math/PI)
=> 3.141592653589793

;; Many ways of getting properties from the System
(System/getProperty "java.version")
=> "17.0.9"
(.. System getProperties (get "java.version"))
=> "17.0.9"
(. (. System (getProperties)) (get "os.name"))
=> "Mac OS X"
(-> (System/getProperties) (.get "os.name"))
=> "Mac OS X"

Even with Swing components, the following codes are equivalent:

JFrame frame = new JFrame();
frame.setLayout(new GridLayout(1, 1, 3, 3));
frame.add(new JLabel("Hello World"));
frame.setVisible(true);
frame.setSize(100, 100);
frame.pack();
(def frame
(doto (JFrame.)
(.setLayout (GridLayout. 1 1 3 3))
(.add (JLabel. "Hello World"))
(.setVisible true)
(.setSize 100 100)
.pack))

If you want to know more about Java Interop, I recommend this article which says that Clojure is a better Java than Java (which I disagree with).

That's all that both languages have in "common". Now you have a whole path to becoming a True and Brave Clojurist.

Conclusion

Learning Clojure, for me, was learning a new language and a whole new paradigm. I've been trying to learn it for some time, and it's not an easy task. But I can say that Clojure forces you to think in a functional way, and leverage immutable data structures, which might be important for a developer to know how to handle properly (especially when things get concurrent).

In the end, this article is not only about Clojure; it's about the experience of a Java developer trying to relate them both somehow and being honest about its challenges and difficulties. Clojure is a very rich language; this is just the tip of the iceberg.

If you want to know a bit more about the language, there is this simple and nice article from Auth0 about Why Developers Should Look into Clojure in general as a programming language.

If you are into it and want to start learning Clojure in a fun way, I recommend reading Clojure for the Brave and True but skip the part about setting up Emacs as IDE and go for IntelliJ with Cursive instead. Unless you want to become a functional nerdola, in this case, configure your Emacs, join the Clojurians slack channel, and subscribe to the Clojure subreddit.

Do you think you have what it takes to be one of us?

At WAES, we are always looking for the best developers and data engineers to help Dutch companies succeed. If you are interested in becoming a part of our team and moving to The Netherlands, look at our open positions here.

WAES publication

Our content creators constantly create new articles about software development, lifestyle, and WAES. So make sure to follow us on Medium to learn more.

Also, make sure to follow us on our social media:
LinkedInInstagramTwitterYouTube

--

--