My “Advent of Code 2020” with Clojure

This is the last of a three parts series of posts about the “Advent of Code”, you can check this post if you want to know about the initiative or this other one for my general experience with the 2020 edition.

TL;DR: using Clojure, I loved the flexibility of the language: solving the problems with a functional paradigm added another (welcome) layer to the challenges. The lack of ready tools has been a minor disappointment, though.

As usual, I hardly even consider non-LISP languages for any leisurely coding, so I picked Clojure for my first participation in the “Advent of Code”.

My choice assumed (correctly) that the competition was comprehensive enough to allow developers with languages as slow as Java to have a chance: anything else would have made “Advent of Code” even more niche than it already is.

The next paragraphs summarize my experience with the “Advent of Code” using Clojure. Enjoy!

Join the dynamically typed side!

You chose Clojure because it’s not Java, then why coding in it as if it was Java?

It’s been a while since the last time I coded in Clojure, and, being a Java developer by day, I felt some (unexpected and unwelcome) uneasiness passing generic vectors and maps around.

At some point, I gave up on the temptation, and I did reify a protocol.

Around day 20, after creating the umpteenth protocol (because what sense does it make to create the C protocol if there are not the A protocol, the B protocol, and since we are at that, the D and E protocols?), I trashed the whole thing.

I restarted from scratches, this time using generic data structures, and never looked back.

Darth Vader "Join Me"

Now, I’m not saying that protocols (and classes, etc…) don’t have their place, but a competition based on daily, ever-changing problems, ain’t it.

And with Clojure, you have a choice.

Functional thinking

aka “that thing I don’t (yet) have”

I guess that if you really miss your C programming and want to make a trip on memory lane, you can start using atoms and do blocks like crazy and, besides producing ugly code, no harm will be done.

If you aim to use Clojure idiomatically, though, you’ll have to solve familiar problems in counter-intuitive ways.

This is a challenge I picked up willingly.

It certainly made me slower but provided some of my best return-of-investment from the competition in technical terms.

It also set my mind at ease about some worries I had about performances: transposing a table using map and apply operations is not as slow as I imagined it to be.

Planning for failure

Mh… It’s not working… Should I have written some tests?

I don’t think so: the scope of each problem was so small that the overhead of testing or TDD didn’t seem to pay for itself.

Also, I can be boring up to a point.

On the other end, not having a safety net (especially with a dynamically typed language) can be a source of minor headaches.

One solution that complements unit testing, is Design By Contract

Have you heard about "Design by Contract"?

I am by no means knowledgeable on the matter, I picked what I know from the excellent book “The Pragmatic Programmer” (Thomas, Hunt – new or old edition, it doesn’t matter).

It boils down to having runtime checks – mostly around functions – to ensure each method receives just what you expect and produces the right outcome.

You can do that in any language but – it turns out – Clojure conveniently supports it with the :pre and :post conditions in the defn special form.

In this (rather uncalled for) use of them:

(defn mangle-list [numbers]
    {:pre [(every? number? numbers) 
           (not (empty? numbers))]
     :post [(number? %)]}
    (reduce * numbers))

the :pre condition makes sure that the input is a non-empty sequence of numbers, and the :post ensuers that the output is a number.

It won’t make a difference with simple examples like this one, but when you are knee-deep in code, pre and post conditions can save you a lot of time.

Tools are not at hand

Let’s profile this thing with VisualVM! (5 minutes later…) Nevermind.

Debugging and profiling with Clojure is not as ready and convenient as with Java. This is a fact.

I’m not saying it can’t be done, I’m saying – again – that it’s not convenient, not at hand (cf. “Simple made Easy” by the man himself), that’s all.

Lack of tools is the most relevant grief I have with the language, even though the language itself is (mostly) not to blame.

That said, if you want to set a breakpoint, CIDER works beautifully (and I absolutely love CIDER, mind you!), but if you want to break on an exception you are out of luck.

On the other hand, you can use VisualVM for profiling, but at the first glance, if you don’t exclude the Clojure namespace first – between that and REPL-related threads – you have to wade through a lot of garbage to find what you are looking for.

Spiderman: "Nevermind, I'll just go..." meme

Conclusions

I had other possible choices for the competition.

Gambit or Guile would have been some excellent candidates if execution speed was of the essence, but I am frankly not proficient enough with them, and they don’t have the JRE with its goodies backing them up.

Emacs Lisp holds a place in my heart, despite being ancient, but the awkward support for dictionaries (or as they call them: “tables”) would have made everything so much harder.

The possibility of picking an entirely new language (Elixir is next on my TODO-list) crossed my mind and was – fortunately – crossed itself. I really dodged a bullet there (not because of Elixir specifically!).

I chose Clojure, fortunately, and – despite some minor hiccups – the combination of REPL-driven programming, support for a rich set of collections, and dynamical approach turned out to be a winning move.

Leave a Reply

Your email address will not be published. Required fields are marked *