Experiencing Haskell

There seems to be a growing interest in (purely) functional languages these days. I've looked at lots of different languages in the past (such as Go, Python, Ruby and others) but never a (purely) functional one. So recently I decided to dig into Haskell, with a view to discovering some of the fundamental differences. I wanted to share some of my findings with you in this post.

The Type System

This is one of the things Haskell is exceptionally good at: types! It has a very strong static type system which is based on Hindley-Milner type inference, that allows the compiler to infer most if not all of the types at compile time. So only in edge cases, or for optimization purposes is the programmer forced to give type annotations. Another great feature of Haskell's type system is its support for type variables. It's Haskell's equivalent of C++ templates, and a feature Go is still missing. They allow for expressive type signatures and contraint based generic typing, i.e.

sort :: Ord a => [a] -> [a]

This says that there's a function, called sort, that takes a list with values of any type, contrained by the typeclass Ord, and then returns a value of the same type. Here the Ord typeclass signifies that the values in the list have to be orderable. Now the a could have also been any other string. But the point is, that just from the type signature it's immediately obvious what transformation the function applies to its arguments.

Because of this expressiveness, there's a separate search engine that will search for functions with a given type signature (regardless of the variables used).

Algebraic Data Types

Another one of Haskell's interesting properties is its support for algebraic data types (ADTs). Think of an ADT as a type-safe version of a union in C. They let you describe data structures in terms of the possible values a variable of this type can have.

data Tree a = Empty
          | Leaf a
          | Node (Tree a) (Tree a)

In this example we describe what a variable of type Tree can be. A Tree is either Empty, or it's a Leaf, or it's a Node (where each node again, contains a tree itself). But that wasn't all, the a is a type variable for the ADT Tree, so this is the implementation of a generic tree-structure.

These ADTs combined with Haskell's powerful pattern matching, allows for articulate function definitions. For example, the following function takes another function and returns a new tree with the same structure as the original one, but with all the values replaced with the output of the function, when passed the original value:

mapTree :: (a -> b) -> Tree a -> Tree b
mapTree _ Empty = Empty
mapTree f (Leaf x) = Leaf (f x)
mapTree f (Node l r) = Node (mapTree f l) (mapTree f r)

As you see, we define the function in terms of the "shape" of its arguments. Mapping an empty tree, is an empty tree, mapping a Leaf, is also a Leaf, but with the function applied, etc.

Be careful though! Algebraic Data Types are not the solution to all problems. Sometimes they make implementing even the simplest data structures, like a doubly linked-list, near impossible.

Code Length & Execution Time

I was quite surprised about this, but from what I've found my Haskell programs are about the same length or even longer as equivalent versions in Python and I take a lot longer to write them, though that is probably due to my inexperience with Haskell's style of programming. But this chart from the Computer Language Benchmarks Game shows that I'm not alone:

Regarding execution time, my results don't match up with the above image. I found (when running a test with a simple log processing script) that the Haskell version performs significantly slower (>200x slower) than the Python version. From what I've heard though, Haskell's built-in list, which is used extensively in the example, is notoriously slow. I should've instead used a faster list data structure like: Data.Sequence.Seq.

Wrapping Up

So you can see, Haskell has quite a few interesting features, that when using them, it feels like you're learning to program for the first time again. Nevertheless you should absolutely try it out and have your mind reprogrammed!