Advent of Haskell
Sun Jan 10, 2021 · 1562 words · 8 min

I have a passion for puzzles. It's very well known by, well, anybody who spent more than 15 minutes with me. And I have a passion for programming as well. I wouldn't be doing that for a living otherwise. A couple of years ago, in 2017, I found out I can combine these two desires by participating in Advent of Code. It is an advent calendar for programmes - every day, you get a puzzle, split into two parts and you should provide a solution. Language, technique, timeframe, everything is up to you. There is a global leaderboard, but it is designed for US try-hards because puzzles are released around 6 am CET. I had a ton of fun doing 2017 in Python and I was very proud to finish it! In 2018, I used to live in London and I had decided to take the opportunity and learn myself a new language. I picked Rust and it was ok, but I gave up when I had to fight Goblins. I wanted to do better in 2019 and I wanted to try out functional programming. After a (too) short research, I chose Scala. And I failed terribly. I didn't like the language, I didn't like a concept of reusing the code from previous puzzles. Therefore, in 2020, I had two goals: finally, finish or at least keep up until the last day and finally learn functional programming. Therefore, encouraged by this article, I choose Haskell.

Learn you an FP for a Great Good

Haskell is a general-purpose, statically typed, purely functional programming language with type inference and lazy evaluation. If you don't understand those fancy adjectives, I recommend checking linked Wikipedia article or Honza's post linked above. I would be just repeating what was said better there. I will rather focus on my own experience preparing the ground for more stories about particular AoC days.

I have to admit I wasn't a complete FP newbie. During my university studies, I went through a Functional Programming course and I loved it. We used Racket and everything was so... logical! I understood recursion, I understood different variable scopes, I understood what are side effects; all these things helped me to be a better software engineer. Which is a very nice thing to say, apart from the fact it would be great to apply all these learnings in a practice. Which I struggled with. FP struck back when I moved more towards system design and I finally saw an amazing talk called Boundaries. The main message is a phrase "functional core - imperative shell", meaning you should aim for pure functions without side effect as the main part of your system and push all tainted functionality to the edge, where it cannot do any harm. My desire to be able to follow this rule was another reason, why I was so keen to invest in Haskell. Similarly to other people, my main resource was Learn You a Haskell for Great Good!. I went with an online version and I have to confirm the book is awesome. It is funny, readable, yet still highly educative. I spent roughly 2 weeks reading it up to chapter 13 and occasionally playing with some commands in interactive REPL.

read $ Advent of Haskell !! 2

First thing I noticed was an ultimately crazy syntax. I really don't know what was the motivation and I still can't understand why, for example, !! selects an item from a list on a specified index. Same goes for /= as not equals, weird list comprehensions and random usage of |. I got used to it, but even after 25 days, I was still looking up even the basic syntactic constructions like list comprehension or guards. And there goes the biggest disadvantage of Learn you a Haskell book - it doesn't have a (good) index. After a week, my head was full of "oh, it was on the page with the weird cowboy cartoon" memories, but looking the page up took me another ten minutes. When I built big enough muscle for remembering function names and packages, the best help was Hackage. Here I disagree with the missing documentation mentioned in Honza's post above. I was able to find whatever I needed. I would love to recommend more sources about Haskell for the even greater good, but I haven't read any (yet). There is a good list in (again) Honza's post. I would like to find time for reading What I Wish I Knew When Learning Haskell, but I will see.

On the other hand, I heavily appreciate syntactic shortcuts removing unnecessary and confusing parentheses (looking your way, LISP) around expressions. For example: instead of writing proofReadArticle(writeArticle adventOfHaskell), you can do proofReadArticle $ writeArcticle adventOfHaskell. $ will just apply a function on its left side to the result of an expression on its right side. Similarly, <$> is a map. It maps a function on the left side to the list on the right side. The most advanced thing I understood was <*>. It is a little bit more complicated, but in general, it applies a list of functions to a list of suitable parameters. If I manage to write the next batch of articles about solving particular puzzles, you will see it in action and understand it better, I promise.

Naughty and Nice

If I can, I would immediately bring a couple of features to more popular languages. Apart from $ and <$> mentioned above, it would be pattern matching. It comes hand in hand with (strong) static typing, but it helps to make your code more structured and easier to read. And it helps with recursion. Another cool thing, I'd appreciate in my day to day job, arises from a simple fact: functions are curried by default. It means you don't have to call a function with all parameters, you can provide just a subset of them. Rather than throwing an error, the function will return a new function expecting the rest of parameters. Only once you provide all of them, the function is executed and you can get the final result.

I don't feel qualified to complain about a language designed by super-smart people. But I still have a couple of features I'd appreciate to be there. For example polymorphism. Many times, I wanted to use multiple functions with the same name but for different types or different amounts of parameters. Another complaint goes to tuples. I abandoned using them a couple of times as there is no easy way for accessing the nth item. I know it probably helps me with a design long-term but for hacky puzzle solutions, it was a bummer. And I am still not sure how do I feel about record syntax. It helps you with a structure of the code but it complicates it at the same time. Because it creates functions named after "properties" of the record. Which means you can easily have a conflict and you could end up using bizarre variable names to overcome their usage in records.

Was it worthy?

My goal was solving more puzzles than last year. Haskell was a tool for it but it gave me a lot of fun and motivation to continue until the very end. It wasn't easy, especially remembering all my beloved Python and Rust for being way more user friendly. But no pain no gain - I consider my understanding of Functional Programming more settled. I am still at the beginning because I barely touched monads or zippers or any other advanced concepts. But the parts I used heavily (functors - <$>, applicative functors <*> symbol, I/O monad) gave me a better understanding of what the FP hype is about. And it's about thinking globally. Or completely. Or just "fully". You do not apply a function just to some data and eventually skip others. You have either everything or nothing. You are thinking about your complete data, not just happy parts. You also learn how to write smaller functions, because bigger ones get out of control so fast you often delete them and rewrite them completely. And you become addicted to a function composition because it just feels so good. You have a function composed out of other functions because all those functions are just transforming data. With a clear typed interface.

I heavily recommend Haskell to everybody who is working on bigger systems. It will teach you better design because there is no other way. I would like to try writing a web application in Haskell. Using 3rd party libraries, writing tests and handling incoming opaque payloads. I would probably struggle a lot, but I would trust the result. Once it gets compiled, it will run.

Thanks for reading. Do you have any questions or errors to point out? Or just wanna chat? Let me know on Twitter.

blog · about · home