About a month and a half ago, I discovered the Advent of Code. If you have not heard of it, it is a series of programming challenges developed by Eric Wastl. Eric is nice enough to provide an advent calendar of programming problems each year, releasing one per day from December 1st to December 25th. The goal is to complete the programming challenge on the day it is released. On the advent of code website it says that people participate for a myriad of reasons including interview prep, company training, and challenging their friends. There are no requirements other than basic programming skills, so many different types of people participate each year and discuss their solutions on Reddit, Hacker News, and other forums.
If you read my last post then you’ll know that I am currently learning Haskell, so I thought participating would be a great way to complete practice problems at a regular cadence. When I started learning Haskell I focused on learning about functors, applicatives, monads, etc. I have since realized that in order to try to use those ideas in Haskell code I actually need to learn some Haskell fundamentals. This post is a journal/log of my experience and some of the concepts I learned along the way. I hope you enjoy!
I had no idea what to expect from myself going into this experience, so the first week was largely trial and error. Honestly, most of the first week I had to look up solutions online and study the code other people had posted. I also wasn’t able to complete each challenge on the day it was released, so some of the problems were done in batches. Below are a few things I learned throughout the first week:
- Haskell’s list comprehensions are quite nice. I had never used them before, and they often were not the most performant solutions, but they were more familiar to me since I had used python list comprehensions before.
- I learned about the
!!operator (link) which is the list index operator. Although it is handy for these small exercises, I quickly found out via the internet that it is not idiomatic Haskell because if the index is out of bounds,
!!gives a runtime error. So we’d prefer to use something that returns
Maybein a real Haskell application.
- One of the biggest hurdles was just parsing the data. I had heard of the parsec parsing library before, but it seemed a bit heavy handed while I was getting started. I ended up mostly using
lines. I decided to try out parsec later on when I was more comfortable with the basics.
- Another struggle I had was trying not to use
whileloops. Coming from an imperative background, I instinctually reached for these constructs when I wanted to process a list. However, going into this I knew it wasn’t the “functional” way so I decided to get some much needed experience using
- I found that in reading other people’s code, it wasn’t immediately clear to me the difference between
.. This stack overflow post sums it up pretty well, and now I’m confidently using both operators for more concise code!
- The final feature that I was able to use was Pattern Matching/Guards. I recognized this feature from my time learning Rust, so I was able to pick them up pretty quickly once I learned the syntax. I think these are useful over traditional if/else statements because they force you to handle all possible outcomes of the expression you are analyzing.
- After 4 days of failing to solve the problems on my own and resolving to studying solutions on the internet, I was finally able to complete days 5 and 6 without looking up the solutions! I was able to use a combination of different things I had learned from the previous days. It was a great feeling knowing that I was making progress and learning.
On day 7 I finally had to use Parsec because the problem required more complicated input parsing. Thankfully I found this tutorial which gave a great explanation! I used day 7 as a learning experience and essentially walked myself through an example I found online so that I would be better suited to use parsec for future parsing problems. It was extremely interesting how each small piece of input corresponds to a single parsing function. Once we combine them together, the parsing code actually reads quite nicely.
Day 8 was another step backwards, as I had to look up an answer and study it again. It’s pretty frustrating because I have ideas for how to solve the problem, but my inexperience with Haskell eventually becomes a road block and I get stuck. I was starting to become more confident after completing days 5 and 6 without looking anything up but I felt like I was regressing. Fortunately I had read before about The Dunning-Kruger Effect from Paul Graham. The Dunning-Kruger Effect is a cognitive bias which, in vastly simplified terms, says that people who aren’t good at a task tend to overestimate their ability. I quite enjoy the graph below that shows the relationship between confidence at a task and skill level as described by Dunning-Kruger. Based on that I knew that I was right around the peak of Mt. Stupid and that my confidence was very much unfounded! It was helpful to understand this effect throughout this process so I had more realistic expectations of how much I knew already and how much I had yet to learn.
Unfortunately, around day 9 I lost the ability to keep up in a timely manner. I began the advent of code thinking I was going to be able to complete every day, but I got busy with work and the holiday. I was somewhat anxious about this “failure” at first, but after a few days I realized it’s much more of a success! Just over a month ago I wrote about how learning Haskell and functional programming was intimidating. However, I’ve already started to break down that barrier! Struggling through 8 (or 16 depending on how you look at it) different problems in a new programming language AND paradigm is no small feat in my opinion.
The way I see it, it was a fun challenge to try and keep up with for as long as I could, but it’s okay that other things come up that prevent me from completing all 25 challenges on the day they are released. There was a bit of frustration, but thankfully I had heard about Dunning-Kruger before, because once I remembered it I immediately tempered my expectations and my frustration dissipated. I’m glad I was able to get started though, and on the bright side I can continue working through the problems on my own time and learning more about Haskell. I was glad that my goal was “write more Haskell code”, instead of “complete all 25 days of Advent of Code in Haskell” because now I can easily classify this experience as a success! This post also serves as motivation to complete more challenges next year.
I hope you enjoyed reading and maybe learned a bit about Haskell as well. I encourage anyone else who is looking for an easy way to test drive a new language to try Advent of Code!
I want to give a few shout-outs, since much of my code is modeled off solutions I found on the internet. Whether I couldn’t get started and needed to learn about some built-in Haskell functionality that would help solve the problem, or if I was able to solve it and just wanted to see how others solved the same problem, I spent a lot of time on r/haskell. The first couple of days, I was able to learn a bunch from Ariel. I also spent quite a bit of time on Fred Strauss’s Github. He had very elegant solutions that I learned a lot from. A special thank you to everyone mentioned above for helping me through this process!