Things I like about Haskell
Here’s a list of some of the things I like about Haskell. It’s in no particular order, and is not exhaustive. There are things I don’t like about Haskell too, but this list isn’t about them.
It’s the opposite of verbose
You can write useful stuff in a matter of lines rather than paragraphs. Likewise if you want to understand some code, you’ve got so much less to read.
Okay, admittedly when starting out with Haskell, it can feel dense and hard to read, but it doesn’t take too long for your eyes to adjust, and for the brevity to become a blessed relief.
A friend of mine once said the Haskell compiler is like a logician sat on your shoulder. I guess this shoulder logician can be one of those people that might not come off so well on first-impressions. Sometimes he seems like an annoying irritating detail-obsessed nerd who is just itching to say “well, actually…” at every little mistake you make.
But the truth is he’s really a great guy, you’ve just got to put a little effort in to get to know him, figure out how you can express your intentions to him, and learn to read his error messages, and before long he’ll become an ever faithful and brilliant buddy who can do all sorts of useful work for you; and above all keep you safe. It’s kind of like having some sort of genius Rain Man type person and ever alert watch dog all rolled into one as your personal programming assistant.
The logician on your shoulder is making sure that everything is defined properly. No side-effects allowed, and all the types of inputs and outputs must be known, and everything must be consistent.
The type signatures, and the type definitions pretty much just tell you what everything is, and there are far fewer surprises. Although Haskell does allow you to throw errors, making problems clear in return types in encouraged. You know that something with a pure signature isn’t going to be doing IO or messing with the database.*
* there are a few exceptions, that mostly involve a programmer deliberately using a function with the word “unsafe” in it’s name.
Sometimes Haskell looks pretty abstract, like with this sort of stuff:
elem :: Eq a => a -> [a] -> Bool
wordyElem :: Eq a => a -> [a] -> String
wordyElem x xs = if elem x xs then "Found it!" else "No, it's not here!"
Yep, pretty abstract. But let’s actually use those functions:
> elem 'a' ['b', 'c', 'a']
> wordyElem 'x' ['a','b','c']
"No, it's not here!"
Despite the ability to define things in the abstract, and combine those abstract things together to make more abstract things, eventually when it comes to using things with some data the Haskell compiler will want to know the actual concrete types the data. By concrete type I mean, it expects to know “the input is a Char”, and it wouldn’t be happy with “the input shaped like this”.
Contrast this with the Java compiler which will allow you to have an object about which it knows nothing except that it implements the Comparable interface for Strings, and so can be compared to other Strings.
This nice, and slightly surprising, thing is that despite allowing you to be hugely expressive in the abstract, ultimately Haskell forces you to think in very concrete and clear ways about your data. And there’s something very solid and nice about that approach.
It’s easy to refactor
Well, this is hugely dependent on how loosely coupled your programming is, but in general the little shoulder logician is a huge help when refactoring. This makes sense, as you can think of your Haskell compiler as doing a sort of unit testing, and the key to good refactoring is having good tests.
Whilst we’re on the subject, I’ve heard it said that an advantage of using a dynamically typed language is that the programmer is forced to write unit tests, which I guess is true, and I have to admit that the test code coverage for a great deal of the Haskell libraries in Hackage is pretty low … but what if I said that an advantage of using a language with an obscure and ugly syntax is that the programmer is forced to write comments to explain it’s meaning? Well, the comparison is slightly facetious, but you see what I mean.
It’s easy to write reliable unit tests
The transparent nature of your input and outputs and lack of side-effects, can make it easier to write comprehensive and reliable unit tests.
It’s easy to maintain
I should qualify. You can easily write terrible sh*t Haskell code that’s a nightmare to maintain; it’s not magic.
But if you combine Haskell’s terseness, and transparency, which make it easy to read, with the lovely helpful compiler and the advantages that brings to refactoring, and perhaps some high quality unit tests if you’re doing it properly, then it’s not hard to see that a Haskell codebase can be easier to maintain that those in some other language.
It’s begs you to model
Mental models are the programmer’s secret sauce, no matter what the language. Just as much as writing a good story isn’t about knowing grammar rules and vocabulary, writing a good program isn’t about knowing languages and libraries.
In any language, when you look at good code it looks like it’s not really doing that much; it looks effortless, and simple, because the approach and way it’s been modelled makes the functionality just easily fall out. Bad code has to put a lot of effort in, it looks big and complex and it seems to be doing an awful lot of stuff.
We all sort of know this intuitively, and it’s why we would expect someone who’s studied maths, science, engineering or philosophy to have an advantage when tackling programming, even though you program most things without needing to know much more than basic arithmetic, and logic. We know that the subjects are kind of similar, but what is it that’s similar about them? They all involve building and working with mental models, ideally ones that are simple, useful and elegant.
Building models is important in any language, but with Haskell that logician on your shoulder is trying his darnedest to force you into it; he wants a model that is coherent and consistent, that he can wiggle around for you, and if he doesn’t feel like he’s getting it he’ll spew errors at you until he’s satisfied.
Now that’s not to say that the model you’ll build will necessarily be a good one, or that you can’t pull certain tricks to evade his watchful eye, like hide under a mahusive monad stack stuffed with IO and global state and pretend you’re programming a declarative language, but at least Haskell is shifting the emphasis from just telling a computer what to do, to creating a model. In short, it makes you think.
It’s actually kind of easy
In all the struggle to create elegant models, and the constant discussions with your shoulder logician, and all the new concepts to grasp and internalise, it often doesn’t feel like it.
But the concepts come, and you fall in love with the shoulder logician, and you eventually hit on something kind of elegant, if you squint a bit, and suddenly you have something small, and nifty, and shockingly simple, and you think “wow, how did that happen?”.
It’s feels good
Who knows why really, but for all the rationalisation, ultimately I like Haskell because it feels good. Best reason for anything.