“Haskell is an advanced purely-functional programming language. An open-source product of more than twenty years of cutting-edge research, it allows rapid development of robust, concise, correct software.” This official definition is only missing one point: Haskell is mind-blowing programming language.
First of all, let’s start with realizing that Haskell is all about purity as opposite to many other functional languages, which do a lot of compromises, like Scala. It could be due to the fact that this language was created by a group of highly educated people, or better to say researches. I think their intent was exactly to create language which would be some kind of definition of a pure functional language.
As per Joel Spolsky, when answering similar question on SO:
“In a pure functional language, you can’t do anything that has a side effect.
A side effect would mean that evaluating an expression changes some internal state that would later cause evaluating the same expression to have a different result. In a pure functional language you can evaluate the same expression as often as you want with the same arguments, and it would always return the same value, because there is no state to change.
For example, a pure functional language cannot have an assignment operator or do input/output, although for practical purposes, even pure functional languages often call impure libraries to do I/O.”
Personally I found Haskell to be the most exciting programming language among all I’ve ever touched. It has many interesting paradigms and features. Most of them are present in other languages, some are not. I would like to briefly go through some of them.
In Haskell there is no function that can accept more than one parameter. What?
Well, of course you can write function
add a b = a + b
But, under the hood it is always translated into 2 functions. One of them is responsible for taking parameter a and applying (+) to result of second function. Second function is there to take first function and apply it on b.
This allows for partially applied functions, which is completely cool stuff.
map ( * 2) [1,2,3]
In above code ( * 2) is actually partially applied function. We are only supplying one parameter instead of two, expected by the * function. So the result of what we get is a function like (x * 2), accepting parameter x.
This function is then applied to the list [1,2,3] one by one, thus we get: [2,4,6].
As another example, we can extract partially applied function from our add to create function, which always adds 1.
addOne x = add 1
So it can be used like addOne 5 to produce 6.
Lazy evaluation isn’t anything new for C# developer. You can think about it as about differed execution in LINQ. Zipping is not something cool, but rather one of the common features of functional languages, but I just wanted to include it here. Below is function every8, which demonstrates mentioned things as well as ranges and recursion.
module Every8thStartingWithSumOfXandY where
everyThird x = x:everyThird(x+3)
everyFifth y = [y,(y+5) ..]
every8 x y = zipWith (+) (everyThird (x)) (everyFifth (y))
You can later use this code to get [4,12,20,28], like below:
take 4 (every8 1 3)
This code below allows you two have a list, which consists with items which are less than 3:
[x | x <- [1,2,3,4,5,6], x < 3]
For example we can match on head and tail of list to perform reverse of list:
rev (h:t) = rev t ++ [h]
Combining pattern matching and list comprehensions we can effectively write quick sort in one line:
qSort (m:r) = qSort [x | x <- r, x < m] ++ [m] ++ qSort [x | x <- r, x >= m]
When I wrote this, I was so much amazed if not shocked about how much Haskell is concise and powerful in expressing complex things. I used to write quick sort many times in C++ back in my university days during programming competitions and even it was easy it never took me one minute or one line to write. (Of course, there are performance considerations regarding two implementations, but we are not talking about them now.)
Don’t be mislead. A lot of other trivial things in Haskell are painful and difficult to implement. To somehow overcome such complexities concept of monads was introduced in functional languages. With them chain of operations can be executed allowing to bypass some data (or state if you will). “Haskell is overly abstract and seems to complicate the simplest tasks, but once you see how monads unify some of the most difficult concepts in programming: backtracking, continuations, error propagation, state management, and parsing, among many others, you can begin to appreciate how elegant monads really are. But to people not familiar with those topics and their difficulty and who have never seen monadic code, monads are just weird math crap.” said Tac-TIcs. Well, for me they are still very difficult even I can read and understand code with their usage. I’ve prepared some other piece of code, which will probably not bring much light on monads, but it shows usage of build-in Nothing and Just. Defining own monads is hard, and worth separate blog post.
module Main where
look key  = Nothing
look key ((k, val):rest)
| key == k = Just val
| otherwise = look key rest
al = [(1, ), (2, [(22, "something deep inside")]), (3, [(33, "else")])]
Which can be used like this:
look 2 al >>= look 22
So you can basically work around Haskell’s restrictions and call methods one by one, using this smarty-looking operator “>>=”. Haskell made easy things hard and then introduced Monads to make those hard things easy again. Now you can write code with sugar do-syntaxes.
I’m too new to Haskell to talk about some advanced features and to write programs which are over 10 lines of code. But from what I’ve tried I definitely like the language. This is language for your weekend’s fun. Play with it!
I hope you liked this short Haskell journey.