Marshall Bowers

Conjurer of code. Devourer of art. Pursuer of æsthetics.

Advent, Expressivity, and Debuggers

Monday, December 2, 2019
836 words
5 minute read

Hello again, fellow interstellar travelers.

I am writing this to you from 34,000 feet in the air as I fly towards Tokyo. I'll try to include some pictures from my trip in the next issue.

It's December, which means that Advent of Code is underway! For the uninitiated, Advent of Code is an annual event during the advent season—hence the name—where you solve puzzles that are released daily. You can use any language you want to solve them (one Advent of Code-er uses Excel to solve the puzzles) and a new puzzle is released daily up through Christmas.

This year I'll be using Haskell as my language of choice for solving the puzzles. My goal is to stick with it for the entire duration, as I usually tend to drop off around day four or five. If you're interested in following my progress, you can check out my Advent of Code repo. Just don't cheat yourself by looking at my solutions before you solve them for yourself!

Personally, I think it's a great way to get your feet wet in a new language. As someone who has a cursory knowledge of Haskell, I've already learned a bunch in the two days' worth of puzzles that I've solved. Things that one might take for granted in another language, like splitting a string on a certain delimiter or parsing a string into an integer, are all things that Advent of Code can help you learn how to do in your language of choice.

One of the things that I've noticed while solving the puzzles is just how expressive Haskell is. For the most part my solutions to the problems read almost exactly like the problem definitions.

Back when I was in college and writing mostly Java code, I remember feeling so powerful when using an IDE like IntelliJ that would take care of so much of the program boilerplate for me. All of the auto-completions and auto-refactorings that were available made it so quick to write and change code, especially when compared to some of the educational IDEs that other students were using.

When I'm using Haskell—or any other language with similar expressivity—I feel that same sense of power. Although now the power comes solely from the language; no additional tools needed. In fact, right now I don't even have an IDE-like environment setup for Haskell. I have syntax highlighting installed with VS Code, but other than that I just rely on the Haskell compiler and REPL when writing Haskell code.

That being said, I do think that language servers and editor integration are great features to have, but I think it is important to make sure they don't become a crutch. I'm sure that there are many developers who are used to using their IDE for everything and would flounder about if they had to write code in a regular text editor and compile their code directly via the compiler.

This same line of thinking extends to debuggers as well. I know developers who would be hopelessly lost without their debuggers and breakpoints. There was a point where I was a heavy user of the debugger, but I try to stay away from it these days.

When faced with a bug, the first thing I like to do is read through the code and attempt to figure out why the bug is happening without running any code at all. In this case, my brain is the only debugger. I would say that in the vast majority of cases this alone is sufficient to diagnose an issue. I can look at the code, make some educated guesses as to where the problem lies, and then I'll usually try to capture those guesses in a test, or just go ahead and apply the appropriate fix. It is only once I have some idea of where the problem lies that I will start actually executing the code and breaking out the debugger, if needed.

I think that being able to reason about programs and debug them in one's head is a very important skill to have. It forces you to consider the program and the bug holistically, rather than applying a fix to what very well may be just a symptom of a larger problem. Using a debugger has a way of pushing you towards zeroing in on just one location in the code and can cause you to lose the broader context.

Another thing that I've come to resent about debuggers is just how bad they are for debugging issues in distributed, asynchronous programs. Asynchrony alone is enough for me to want to stay away from a debugger, as things like breakpoints firing in an unexpected fashion can lead to a very confusing debugging session. I find that "printf debugging" tends to be much more effective for these types of programs.

Until next time,

Marshall