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.

My “Advent of Code 2020”, in review

If you don’t know what the “Advent of Code” is, you can check my previous post on the topic.

Getting in the top 100 requires a kind preparation, and mindset I don’t have.

It’s the kind of skill you train when you routinely compete in online events where the super-fast win and, while I respect it, it’s not the approach I like when coding.

I golf to be deliberate and thoughtful, and coding clean instead, so I took the competition at my own pace, with only one exception: I tried to solve each problem in its own day.

Here is my take-away on the competition itself, which I’ll hopefully follow with an entry about doing “Advent of Code” with Clojure.

Even one problem a day can be too much

Having a regular job and a family, I often started the day’s problem at 10PM, only to finish it at 3AM.

Maybe other people are faster than I am, but for as long as I managed to ride the wave of each daily problem, it’s been taxing.

It’s fun (most of the time)

You wake up and you are being treated with a new, absurd MacGyver-esque problem. You wrap your head around it, find an elegant solution, and give yourself a pat on the back.

Some problems required more time to crack than others, it’s hard work sometimes, but the lasting feeling is rewarding.

This is what happens on a sunny day.

It’s less fun when you remain stuck

Day 23 turned out ugly. I got seriously stuck for two days on a problem that – I realized afterward – was embarrassingly simple.

From the statistics on the website, I know all other participants are moving forward. The leaderboard doesn’t lie: I’m among the few who are finding that problem a challenge.

At some point, I literally felt like the last sucker: I personally questioned my value as a developer. And I was still stuck, so I had to bite the bullet and check Reddit for other people’s solutions.

It turned out the problem was among the simplest ones, and right because the solution was so simple, I had pre-emptively discarded it a priori.

I won’t lie: It did hurt. A LOT.

Finishing the remaining two problems 2 days late didn’t really deliver much joy or a sense of accomplishment, but I tried to put the experience in perspective.

In hindsight, there are some considerations that help to mend my ego a bit, but that can have some sort of general value.

First, I may have been trailing after the group, but it was still a small group of people who don’t just code but also love so much doing so, it solves coding problems while on vacation. So yes, I may even have been “the last”, but I’m definitely not the last sucker.

Second, of the almost 150000 people who started participating only 8% managed to finish it: so even if I was among the slowest 10% of people to solve day 23, I’m in the top 8% who didn’t give up!

The best day was the worst day

More importantly, however, day 23 has been the most enlightening day.

Maybe the other ones may have been more pleasant, and they may have even managed to let me think and improve a bit, but thedebacle of day 23 highlighted a fundamental flaw in my way to approach problems in general, and hopefully managed to get the point across.

It did also show my shortcomings in responding to failures, in general: letting myself feeling down, grudgingly pressing on, losing the sense of perspective are all pains I could have made without.

I picked up the (wonderful) “Thanks for the Feedback: The Science and Art of Receiving Feedback Well” (Douglas Stone, Sheila Heen) again to re-read the chapter about “giving yourself a second score”.

The idea is that failures are not always under our control, but the way we react to them is and, like in this case, so it’s the way I react to my failure to react to failure.

I also refreshed a bit another favorite book of mine: “The Subtle Art of Not Giving a F*ck, A Counterintuitive Approach to Living a Good Life” (Mark Manson).

I didn’t re-read it all, I just picked two sentences:

“Pain is part of the process”

and

“Failure is the way forward”

And only God knows how much pain and failure I face every time I code…

About the “Advent of Code”

Ok, it’s too late now, but say it’s Christmas: vacations are looming, plenty of time to focus on your project, but what if you don’t have one?

Eric Wastl must have felt this pain: he’s the mind behind the Advent of Code competition, a friendly and geeky website where each day of the advent a new computing problem is unlocked.

The puzzles are bound together with a backstory: maybe you are rescuing Santa and saving Christmas or – like this year – trying to make it to your resort for a vacation.

It doesn’t matter: whatever the backstory could be, you will end up facing new zany situations every day, the solution of which will require (you guessed!) computing and logical skills.

The puzzles have two parts: the first one is usually straightforward, while the second part can be tricky: it may require some specific optimization techniques, some math, or just intuition.

Each problem/day solved, a new part of the ASCII-art calendar is also unveiled: a thing of irresistible geekish and childish beauty!

The narrative is witty and fun, and the problems are engaging (most of the time). Old calendars remain open, so you can still solve all the old challenges.

Even if it’s not the advent anymore, you can still wake up, read a problem, and set yourself the goal of finishing it within the day, maybe competing among friends on a private board (the site has those).

It’s a great project: I’m impressed about how much care has been put into the narrative (even if it’s absurd…) and the details, so whether you are bored or are looking for some new kata to practice a new language, make sure to check it out!