Being someone who programs but also values my sanity and doesn’t work on anything as important as mission control systems, the #1 way that I evaluate how much I like a programming language is usability: do I enjoy using it?

But I’ve never found something that seemed completely intuitive to me.

And the worst of everything was Haskell. I tried learning it a few times because–in theory–it seems so damn cool.

But actually using for a real project seemed kind of futile. It feels like you’re literally fighting with the compiler sometimes, and the syntax is extremely foreign to someone who came of age using C family languages. At least when you first start.  There are so many times when I’m going through exercises and think — yeah I know exactly how to do that:  I’ll hash this, then run so and so’s algorithm on it… but then you can’t even get I/O because that’s a Monad or something.

Today, I was going through yet *another* Haskell book, Chris Allen’s Programming Haskell From First Principles, and I finally did an exercise that made it click: Binary Tree Traversals.

Here’s a Binary Tree data structure:

This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.

 data BinaryTree a = Leaf | Node (BinaryTree a) a (BinaryTree a) deriving (Eq, Ord, Show)

view raw

binaryTree.hs

hosted with ❤ by GitHub

And here are all the traversals:

This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.

 — Do Tree Traversals and Built a Visitation List for each preorder :: BinaryTree a -> [a] preorder Leaf = [] preorder (Node left root right) = root : preorder left ++ preorder right — NOTE: Need to use the ++ so each list gets built separately and then concatenated — after it hits bottom inorder :: BinaryTree a -> [a] inorder Leaf = [] inorder (Node left root right) = inorder left ++ [root] ++ inorder right — NOTE: Need to put root in its own list so we can concatenate postorder :: BinaryTree a -> [a] postorder Leaf = [] postorder (Node left root right) = postorder left ++ postorder right ++ root : [] — Similarly, here. Need to Cons root onto a list, since it'll be the bottom value

You don’t need to make stacks and queues or keep track of what’s been visited. Instead it’s like drawing a picture. You can pattern match everything, literally placing the values where you want them to  *visually* end up in the resulting list, and it gets built up through the natural recursion. Each recursive call builds its own sublist, and then–only when that recursion is finished–the resulting list gets added into the bigger one which is eventually returned. The simplicity is kind of mind blowing.

But what’s interesting to me  is that getting to the point where this makes sense takes so long. So much frustration. And then out of nowhere, it’s all so simple. Haskell is the only programming language I’ve ever learned where I felt that way.

Most of the others have what you might call linear learning curves. Some are steeper than others, but you see results immediately. With Haskell, you read three books and still don’t totally understand what the hell is going on. Then, you’re doing a random exercise one day, and not only do you  “get” it, but you realize it’s amazingly simple. I don’t know if that’s good usability. But it’s a unique experience, and one that’s very satisfying.

So I guess what I’m saying is this:  Don’t give up if you’re trying to learn this stuff. When you least expect it, everything will click. And then you’ll wonder why it took so long.