Back to Haskell after 14 years
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
:
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):
You have to express it with guards, so this is how to do it:
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:
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:
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:
As getName
returns IO String
, getName2
will as well, so you cannot force it to return String
via explicit type declaration.
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.