When I started my Bachelor in Computer Science in 2004, we had a programming lecture where we mostly learned to program in Java. But we didn’t only learn Java, but also had a few weeks where we learned a bit of Haskell. I enjoyed programming in Haskell at the time, but couldn’t understand at the time how I could use this language to solve real world problems. Now more than 14 years later as a more experienced developer who likes functional programming, I decided to try Haskell again as I wanted to see how it’ll fell like to solve a bit more complex problems with it. And of course, I wanted to know what Monads are and understand them, as that’s what Haskell is known for, but we didn’t touch this subject when I learned Haskell at university.

How to get started with Haskell

Just install the GHC (Glasgow Haskell Compiler) which is the most commonly used Haskell implementation

Haskell has an interpreter that allows to run code in a REPL

One great thing about Haskell is that it has a REPL called ghci that allows an interactive development experience as you can test some expressions and functions easily. It already had this REPL when I first used it 14 years ago (probably much longer) when this was not yet usual like today.

I must admit that when I was a newbie programmer the main reason why I thought that Haskell cannot be used to write real programs was that I thought that ghci was the only way to run Haskell programs.

Haskell can be compiled to native code

Of course you can execute Haskell programs in other ways, the most common is to compile your program with GHC (Glasgow Haskell Compiler) and execute the native executable that the compiler creates.

You can build GUIs and Web sites with Haskell

There are a also GUI library and most importantly web frameworks, so my assumption that only console programs are possible in Haskell was (of course) completely wrong…

There is an Exercism learning path to learn Haskell

Back to the present to get going with Haskell, I started doing exercises on Exercism. I didn’t have a lot of problems to get going with the language as I had learned a few other functional programming languages (e.g. Clojure and Erlang), so this didn’t caused problems to me. The syntax is pretty simple, it’s a bit like Clojure without brackets :)

Haskell has pattern matching

What I especially like is the pattern matching to write functions, a bit like in Erlang or Prolog, but slightly less powerful as you cannot match without guards that 2 parameters have the same value. As example the classical recursive function factorial:

factorial :: Integer -> Integer
factorial 0 = 1
factorial n = n * factorial (n - 1)

I like it that you simply specify the base cases written like this without needing to write a different case.

What isn’t possible unfortunatly is something like this (would be in Erlang or Prolog):

contains [] x = False
contains (x:xs) x = True
contains (x:xs) y = y `contains` x

You have to express it with guards, so this is how to do it:

contains [] x = False
contains (x:xs) y
    | x == y = True
    | otherwise = xs `contains` y

Haskell functions can be called with prefix or infix notation

Note that I demonstrated another cool feature of Haskell: you can call any function with two parameters in two ways, either infix style like I did above: xs `contains` y or the usual way as prefix function: contains xs y. For arithmetic operators like +, -, * and / the infix notation is the default, but you can use the notation (+) to call it as prefix function, so for example 23 * 44 and (*) 23 44 are equivalent. This is one thing I especially like in Haskell in comparison to Clojure, as I must admit, that I still make some mistakes when using the comparison operators in prefix notation, like in (< 4 x). You can choose the style you prefer for each function call. Note that the contains function doesn’t need to be implemented, as it already exists as part of the standard library, but with the name elem and with flipped arguments.

Haskell has partial functions

Another cool feature is that you can only partially apply function without the need of a lambda expression. For example to add 3 to all elements of a list, you can use the following expression:

map (+ 3) [2,3,4,5,6,89,45,34]

Haskell has strong typing and type inference

What is really cool as well about Haskell is the combination of strong typing with type inference. You usually don’t need to declare the type of your function (though it is encouraged to do so) and the compiler will derive the correct type for your function. So you can have the ease of dynamic languages combined with the correctness of strong typing.

Haskell can handle IO

One thing that caused a doubt about Haskell as general purpose language was that we never learned how to work with input and output at university, so I didn’t know how to do it at the time. It also takes quite a while in all tutorials until you do it. The reason for it is that IO actions like reading and writing from/to standard input/output or files has to be done in a special context. You cannot just print out the value of your variables inside your (pure) function. I will demonstrate what I mean with a small slightly more advanced “Hello World” program:

import System.IO

main = do
    name <- getName
    putStrLn $ "Hello, " ++ name ++ "!"

getName :: IO String
getName = do
    putStr "What's your name? "
    hFlush stdout
    name <- getLine
    return name

So here you see how we need to write code that accesses IO: you have to put them into functions that return IO <actual return-type>. Functions with an IO return-type cannot be accessed inside “normal” functions.
The expression name <- getLine puts the return value of the getLine function which has type IO String into the variable name of type String. The variable name can be passed to pure functions. In this case we just return the value.
The <- operator is only available in IO actions. Using the keyword do we can perform a sequence of IO actions and the last one will be returned. The keyword return is a function that wraps a normal value (a String in my example) and makes an IO String out of it.

These functions wouldn’t work:

getName2 :: String
getName2 = getName

As getName returns IO String, getName2 will as well, so you cannot force it to return String via explicit type declaration.

getName3 :: String
getName3 = 
    name <- getName

This won’t work as well. As soon as you have the <- arrow operator, you cannot use it in a function that has a return value that isn’t IO <some type>.

This approach has the big advantage that code having side effects is marked as such and that it is in your interest to carefully consider which functions need to perform IO actions and which not.

You can write imperative programs with Haskell

Inside do blocks you can more or less program in a imperative way, so if you thought that the functional style prevents you to perform some tasks, you still have a tool in Haskell how to perform them. That is one more reason that demonstrates that Haskell can be used as general purpose language.

Haskell has Monads

By the way, IO is a Monad. Monads are the language construct that allows the usage of the <- operator and do-blocks. I will write a bit more about Monads in a future blog post as this is a pretty interesting concept that we don’t have in most languages, but that cannot be explained easily in a few sentences. To explain it oversimplified, Monads allow you to execute functions on values wrapped in some container and the result will still be wrapped in this container. In our case IO is our container and we perform operations on the string wrapped in the IO Monad.

Haskell has other advantages

Finally a few more reasons why you can consider using Haskell for your future projects:

  • there are great web frameworks available for Haskell, Yesod being the most prominent of them
  • you can run Haskell code in the JVM using the Haskell implementation Eta
  • templates allow you to extend the language

My personal conclusion regarding Haskell

For me personally, I really rediscovered Haskell and pretty much enjoyed programming with it. I’ll use it for some projects in the future and see whether I can entirely prove to my younger self that there is nothing you cannot do in Haskell. I’ll keep you updated on my progress in Haskell.

Interested in learning Haskell?

If you want to try the language yourself, I can recommend you to do the Exercism path of Haskell and if this is not interesting or challenging enough, you can try to implement some of the exercises of Gophercises in Haskell instead of Go.