Courting Haskell

  • 19 min of reading
  • Stats

In the past two months I've been trying to learn the Haskell programming language. It's vastly different from anything I know, so it served me also as a way how to empathize with complete beginners to coding. This is a diary from my journey.

Triangles
Photo by Paweł Czerwiński

First sight love

I spent my university years juggling studying, freelancing, and partying. To accommodate all three, some sleep included, I decided to reframe courses as "tours into potentially interesting topics". Regardless whether I liked a course or not, I formally finished it with E, which was satisfactory to continue the school. E as in EFFICIENT.

Personally, I categorized the topics into three sets. Respectable ones, which are noble and beautiful, but too hard for me to get my head around them (e.g. math). Intriguing ones, which I wished to dive into later in my life (compilers, functional programming…). And finally, topics I never wished to see again, ever (e.g. hardware).

A superficial experience with Haskell programming language in one of the courses got stuck in my head as a divine encounter. Since then I have secretly desired to explore it further. It seemed to be as noble and pure as math, but unlike math, I felt there is a chance I could be able to actually learn how to do it without too much pain.

What is Haskell?

Haskell is a statically typed, purely functional programming language with type inference and lazy evaluation.

My first useful experience with programming was with PHP 4. Later I learned PHP 5 and its object-oriented programming concepts, dreaming about switching to Java one day. But then I've encountered Python and it changed everything. Unlike PHP, it seemed to be "purer" and "nicer". At that time, PHP was a mess. Also, unlike Java or PHP (which at that time started to converge to Java), it seemed to be "simpler". It wasn't forcing me into so many unnecessary layers and abstractions (and types and classes). In my eyes, Python was noble, pure, divine.

Over time I learned about Python's warts and imperfections, and my old memory of Haskell from university years gave me an irrational expectation that these things wouldn't happen there, because Haskell is without compromises, it's divine, for real, it's the purest of all programming languages, invented by scientists…!

As of September 2019, Haskell was the 23rd most popular programming language in terms of Google searches for tutorials and made up less than 1% of active users on the GitHub source code repository.

Haskell isn't exactly popular. It's partly because it's exclusively functional. Most programmers start with imperative languages, perhaps spiced with object-oriented programming here and there. For someone coming from Python, Haskell is like from another world, which turns the learning into a tough experience. Another reason might be that unlike more mainstream functional languages, such as F# or Scala, it's not tied to any existing ecosystem, such as those of C# or Java.

Some people also say it's so academically pure that it's impractical and that it's too math-like. It's, indeed, heavily founded on mathematical theories (category theory and lambda calculus). You'll stumble upon whiteboards with diagrams, and even the most basic materials won't avoid using strange terms, such as monoids, monads, or functors. Check videos from Bartosz Milewski as an illustration of what I mean:

Bartosz Milewski's YouTube channel

I don't say theory and whiteboards are bad, but it's not the usual experience when learning a new programming language, and for many it can be a bit intimidating.

This all causes people to think Haskell (perhaps together with Lisp) is some sort of sorcery. First, Haskell and Harry Potter both start with H. Second, nobody truly understands why it all works, perhaps only the most mad matematicians powerful wizards. Third, the apprentices (nerds, in the eyes of the majority society) are encouraged to use eerie syntax script and to speak in mathematical terms cast bizarre spells, such as monad! alohomora!

Being aware of these things, I kept my desire for Haskell as something I'll probably never really pursue and I focused on mastering Python and later JavaScript as my tools of trade.

FP sneaking into my life

I think it started in 2018, when Vladimir Gorej joined Apiary. Vladimir is a functional programming aficionado with strong theoretical background, who introduced us to Ramda and Ramda Adjunct, RxJS, and more. Suddenly, FP was all around me.

We attended the LambdUp conference. I started to write my imperative code with more pure functions, immutability, and with maps, filters, and reduces in mind. I started noticing how widespread Ramda is, how popular Elm is (I first heard about Elm at Devel.cz 2016 from Richard Feldman), and every day I kept being reminded about Clojure on Twitter by Aleš Roubíček. Moreover, one of my other colleagues, a C++ genius Thomas Jandečka, couldn't pass a kitchen small talk without mentioning how powerful Haskell is.

The Grand Budapest Hotel

Yeah, pun intended!

The last day of October 2019 was my last day in Apiary. I left to have a break from work for a few months and to have time to start junior.guru. Fresh to my sabbatical, in November I accompanied my wife on a business trip to Budapest. The weather was bad and I didn't feel entirely healthy, so while she was enjoying a conference, I ended up slacking in our hotel room. And I was quite in a mood to learn something new. What about finally learning the real deal?

Yeah, I could have chosen something everyone else is learning, like React or Go, but I didn't feel like being actually productive. I had a unique opportunity to learn anything, regardless its usefulness. So I decided I'll finally take a look at Haskell. Also, where else to start learning an obscure language if not in Hungary?

So I started. I knew Learn You a Haskell from the times of my university course, and I decided to give it a chance, because I remembered how the naive cartoons helped me to feel comfortable even around words like functor.

The list monster
Cartoon by Miran Lipovača, taken from An intro to lists

After a few hours of continuous reading, I felt like sharing my immense success and as a true millennial, I concluded my day with writing more Facebook comments about how I learn Haskell than writing actual Haskell code.

I didn't stop learning though. Even after returning from the trip, I tried to find some time here and there to continue with the book. And it was a strange experience. After quite long time I was back in learning something this intensively. Also, as Haskell is so different from anything I know, more and more I felt like I'm able to empathize with programming beginners. That's usually very hard for a person who's coding for ~20 years. Even though I'm spending my time around beginners, helping them, and mentoring them, I didn't ever get as close to their troubles as I did when learning Haskell.

There are two types of materials for two types of learners. Some people like to first read and then try out stuff, some people require very practical, hands-on learning from the very start. I'm the reading type, so I didn't have any fundamental problem with LYAH as a learning material, but I have to admit the fact that Hello World is in Chapter 9 did feel a bit intimidating. It's not the book's fault though. Printing text is a side effect and in a strictly pure language, you better read 8 chapters before you start with side effects!

Advent of Code

My friend Míla told me about Advent of Code, an advent calendar for puzzle solvers. Every day in December there's a programming puzzle and it's up to the solver how they come up with an answer, so it's a good opportunity for trying out new programming languages on small exercises. Programming puzzles aren't usually my thing, but this coincided with my Haskell learning, so why not? Apart from Míla with Scala, I noticed other people joining: Czechitas director Dita Přikrylová with Python, my ex-colleague Stephen Mizell with Racket, and more. That motivated me to try it out. Except it was 2nd of December and I didn't even know how to print text in Haskell yet…

Albeit late, in the end I started with the puzzles. They usually contained some test inputs and expected outputs, so I thought about solving the puzzles in a test-driven way. I didn't want to dive into installing Haskell packages yet and I didn't find anything for testing in the standard library, so I decided to go with writing small CLI programs and verifying them with bats. Bash tests also have the benefit that they will work with any language, if I decided to go with a different one later. Following the example of others, I've set up a repo with my solutions.

The first puzzles were okay. It was nice feeling to write my first Haskell program, and second, and third, and to practice the alien syntax and the functional approach. In the beginning, I felt like using Haskell for the puzzles is cheating. I just declaratively wrote down what I wanted and I got it. However, the puzzles got quickly harder and I found myself spending several hours per day solving them. And I don't think the language is to blame. I'd probably have the same issues when solving the problems with Python, which I know very well.

As I mentioned, programming puzzles are not my thing, and usually they cause me more pain than joy. I used to play Interlos, an online puzzle game organized by people from FI MUNI, but I bailed out a few years back when I realized that each year, despair is the overwhelming emotion I'm getting from participating (in contrast with games for non-programmers such as Sendvič, which I can enjoy). I guess I'm not a "puzzle solver". I rather think of myself as a "project builder".

Well, enough of excuses. The third day I spent 7 hours to solve the puzzle, and, despite my success, I felt like shit for the rest of the evening. I decided there are perhaps better ways to learn Haskell - or to spend my sabbatical, when we're at it. I dropped out from Advent of Code, and I soothed myself by reading functional.christmas, a nice resource with no annoying puzzles! someone sent me around that time.

Although I didn't continue with it, Advent of Code definitely gets credit for helping me to start writing my first Haskell programs. The first puzzles were exactly the sort of tasks I was able to solve with my very basic knowledge of the language.

First real-world project

I wasn't sure Haskell is going to be something I'll be doing from now on, but I didn't want to stop learning it until I build a real-world project. At least a small one, to really see how the language feels in practice.

A few months ago my friend Ivan Kvasnica, who's proficient mainly with PHP, wanted to try out a new language. Python, or perhaps server-side JavaScript. I recommended him to learn syntax at Learn X in Y minutes and to build a small project, as I think it's the most efficient way of learning (see the theory behind it). In the end, Ivan implemented one of my silly ideas, and wrote a Bude Hezky? bot in Python. It's a bot which checks weather and messages me 7pm every day if I should go cycling tomorrow.

I'm so good in advising others! Well I wonder why I didn't start with Haskell at Learn Haskell in Y minutes?

Anyway, I was looking for a small project idea. Some of my friends often forget when exactly the Prague Python meetup takes place (even though it's regularly on the third Wednesday of each month), so I decided to test my Haskell skills on a bot which would send out meetup reminders.

The advantage of a project like this is that it's a small CLI app, and you don't need a sophisticated runtime to deploy it. CircleCI or GitHub Actions allow you to run code in basically any language, and they both support running your code on a schedule. So all you need to do is to put your code inside a GitHub repo and configure how, and how often your app should be executed. CircleCI has a ready-made tutorial on how to set up a Haskell project, so it's easy to get started even with a language you don't know very well yet.

Despite having the tutorial, I spent most of the time on understanding how to have a working project with Stack. The tool is one of the most documented things I've encountered in Haskell ecosystem, and still, even after a lot of reading, I wasn't quite sure how it all works, and how to properly work with dependencies. Learning the packaging took away my excitement and I caught myself procrastinating the project. To be honest, I'd say learning Python packaging probably causes the same to people. Taking my hat off to npm, which has an amazing developer experience, especially from the beginner's point of view.

I tell beginners to look for inspiration in Open Source, and that's also what I tried when setting up my project. Unfortunately, as Haskell isn't very popular language, there is just about two real-world projects on GitHub: Pandoc and Semantic. Perhaps I'm bad in searching GitHub, but I really couldn't find much else. I don't know where code of all the packages at Hackage is hosted.

In the end, I got the best advice from the Alexis King's blog. There are several articles tagged Haskell, but I'd definitely point out An opinionated guide to Haskell in 2018 and Four months with Haskell. Later on when I was done with the app setup and I worked on processing dates & time, A Haskell Time Library Tutorial by Christoffer Stjernlöf served me as a great resource.

It's done and it's working, so if you use Telegram and you want to know that the next Prague Python meetup is about to be soon, subscribe to the https://t.me/pyvopraha Telegram channel.

How does Haskell feel?

Powerful! It takes just a few lines to implement your own clone of Vim:

import Control.Monad

main = do
    line <- getLine
    when (line /= ":wq") $ main

To run this on macOS, first run brew install ghc to install Haskell. Save the code as vim.hs, then execute runhaskell vim.hs. Done! You can type characters and finish each line with the return key. The program will keep confusing you until you exit by typing :wq.

Latin

Okay, now seriously. Learning Haskell feels like if I was a Chinese and I decided to learn Latin. It's not very practical, but it gets me a strong foundation for learning almost any contemporary European language, and it gives me an intro to the cultural background. I'd say Haskell and Lisp are like Latin and Greek of functional programming. Many other functional languages adopt Haskell's theory, naming, syntax, type declarations, etc. At the LambdUp conference it felt like if an F# person wants to talk to a Clojure person, they talk in Haskell. When I went through this issue of functional.christmas, I was surprised that the Elm code examples were almost valid Haskell.

Math

If SQL or Python read like an English sentence, then Haskell reads like math. Feels like math. It is math. It's like if someone copied a page of your university math course workbook using only ASCII letters, and saved it as .hs. The /= operator is much closer to ≠ than !=, which can be found in other languages. Naming functions foo' is completely normal in Haskell, even inside the standard library. You know what I mean. Basically, you see the code and automatically your head reads it with your math professor's voice:

parseDay line equals fromGregorianValid y m d, where y equals…, m equals…, and d equals…

This can be a bit challenging for people who have always been bad at math, like me.

Types

One thing, which is very different from what I know, is types. It's a long time since I've been doing anything in C, C++, or Java. Most of my career I'm writing PHP, Python, or JavaScript. Haskell is a strongly typed language, and designing a program in such environment can get very different from what I'm used to do in Python. I didn't really play with TypeScript much and I'm yet to try out Python's type annotations. In Haskell, types are everything. Read the Parse, don’t validate article by Alexis King and you'll get what I mean. So far I'm a noob around this. I need more practice to learn how to use types as a powerful, active element of my program, not just as something I'm trying to get somehow right so my code compiles.

Motto

The same way Python has its Zen, Haskell seems to have the following motto:

Worst practices should be hard

I found a good explanation of the motto in an unsurprisingly titled article Worst practices should be hard by Gabriel Gonzalez. To me, it feels like a very powerful concept not only when designing a programming language, but when designing virtually anything.

Downsides

Haskell definitely feels very powerful and elegant. There are some parts of it though which I found, ehm, suboptimal.

The String vs Text dichotomy is very annoying. It's very well described in the chapter The String problem of the Four months with Haskell, so go an read it.

The ecosystem feels like a mess. Cabal, Stack, packages, it all seems everything but straightforward. I couldn't get my head around how it works. Why Stack downloads half of the internet to my computer before installing any packages? Why it then refuses to install a package I found on Hackage? Very early I got into some version mismatches or whatever it was, without any luck finding out what's the problem and what is the preferred way to solve it.

Which brings me to no documentation. And when I say no, I really mean none, nothing, nada. The most annoying downside! It seems Haskell people consider type declarations to be enough prose to be considered as documentation. That feels very, very uninviting. I'm fine reading type declarations, but sometimes I just need more explanation, perhaps even a tutorial. Well, most of the packages I found had nothing but a heading in their README! Not even a single usage example. Apart from trying to stumble upon random blog articles on the internet, I was basically left with reading code, or with trial and error. Some people tell me Python docs aren't sufficient. Well, from now on I'll be sending them to check out Haskell. They'll return crying and begging for forgiveness!

Also, I'm super scared of the lens library. It's implementing the concept of lenses, which I'm okay with, but in a way that it seems like a separate language within Haskell. Luckily, I didn't need to use it much.

Try Haskell

How can you start playing with Haskell? Installing Haskell on macOS is easy with Homebrew:

$ brew install ghc

Originally, I didn't know at all that Haskell can be also interpreted. You can run your first program in a similar manner as your first Python program:

$ runhaskell hello.hs

This helped me a lot in the beginning. Haskell also has a REPL, which you can run as follows:

$ ghci

If you only want to try Haskell without installing it, you can paste your code to tryhaskell.org.

Resources

If I was to continue with learning Haskell, I'd probably dive into the following:

What next?

Honestly, I don't know if I'll continue learning Haskell. I didn't have a clear goal when I started with it. I wanted to get know better something I thought is divine for many years. I expected a hardcore intro to functional programming. I don't think anymore that Haskell is divine, and I definitely got a good intro to FP. I think I could have spent more time on playing with Haskell's types and properly handling all corner/error cases.

Perhaps one day Haskell will compile to WebAssembly and that will make it more popular, but meanwhile, I think I'll take a look at something else. Elm? Clojure?

Perhaps courting Haskell won't work out in the end, but I'm pretty sure that I'm not about to stop courting functional programming any soon. Did you know there's a whole page on functional programming in Python in its documentation? I didn't!

Update: Commenters also mentioned simplehaskell.org, Practical Haskell: A Real World Guide to Programming, and Haskell Programming: From First Principles.