Haskell series part 2

This is the second article of a series on the functional language Haskell for beginners

Haskell series part 2

Thank you for joining us for the second part of our Haskell series, you will find the previous article here where I give a quick introduction to Haskell's types and functions.

As promised, what we are going to cover in this article are: lists and function declarations. Let's start with the lists as we will need them in the next part:

Lists

Lists are essentials in Haskell (and functional programming in general) and are the most commonly used data structure:

Prelude> a = [1, 2, 3, 4, 5]
Prelude> head a
1
Prelude> last a
5
Prelude> length a
5

A few functions comes out of the box to help us with Lists:head returns the first element and last the last one (be careful, it is not  tail, tail will give you the whole list minus the first element). Then length returns the number of elements in the list. Easy peasy.

Lists are very versatile, you can simply concatenate them together like so:

Prelude> a = [1,2,3,4,5]
Prelude> b = [6,7,8]
Prelude> a ++ b
[1,2,3,4,5,6,7,8]

You will note that in Haskell we differentiate + (add) and ++ (concatenate).

In order to access a specific index you can use the following:

Prelude> a
[1,2,3,4,5]
Prelude> a !! 0
1
Prelude> a !! 1
2

And based on what we learned above about concatenation, you can add an element at the beginning or the end of a list like so:

Prelude> a ++ [6]
[1,2,3,4,5,6]
Prelude> [0] ++ a
[0,1,2,3,4,5]

Lists comprehension

Now, let's have a look at list comprehensions, if you are coming from Python you will know what's up. If you do not, think of it as an expression to iterate through a list and apply some operations to obtain a new list.

Let's do a comparison Haskell/Python where we are going to double every element in a list:

Prelude> a = [1,2,3,4,5]
Prelude> [x * 2 | x <- a]
[2,4,6,8,10]
Example in Haskell
>>> a = [1,2,3,4,5]
>>> [x * 2 for x in a]
[2, 4, 6, 8, 10]
Example in Python

Looks pretty similar, the expression is wrapped in square brackets. In the Haskell version we use a pipe | to split what we will do to each elements of the list (left) and what is the source of the list (right).

And now, we would like to apply a predicate (which is essentially a filter - it is a very common thing to do) so we do not take in account, let's say, the number 3:

Prelude> a = [1,2,3,4,5]
Prelude> [x * 2 | x <- a, x /= 3]
[2,4,8,10]
Example in Haskell
>>> a = [1,2,3,4,5]
>>> [x * 2 for x in a if x != 3]
[2, 4, 8, 10]
Example in Python

You will note that in Haskell, we use /= to check for inequality instead of the usual != in most languages. One more thing, in Haskell predicates are separated by ,  meaning that you could put many of them instead of writing elif, or and so on. For instance:

Prelude> a = [1,2,3,4,5]
Prelude> [x * 2 | x <- a, x /= 3, x /= 4, x /= 5]
[2,4]

Functions declarations

In our previous article, we had a quick introduction about functions, as a reminder we wrote the following:

powerOfTwo x = powerOfX x 2 
powerOfThree x = powerOfX x 3 
powerOfX x y = x ** y

Those are simple functions to calculate the power of a number.

In Haskell, you could only write function implementations in theory but it is a good practice to write definitions with it. After all, it is a statically typed language, it would be a huge loss not to mention the types of your functions.

Let's write one for powerOfTwo in our file fn.hs:

powerOfTwo :: Float -> Float

Nice, but before jumping to conclusions, let's define powerOfX:

powerOfX :: Float -> Float -> Float

Right, so basically, we do not differentiate parameters and return value. Which we will explain later on. As of now, let's just remember that the return value is the last one on the right.

And of course we can check our functions by using the very helpful :t like so:

*Main> :t powerOfTwo
powerOfTwo :: Float -> Float
*Main> :t powerOfX
powerOfX :: Float -> Float -> Float

So, now that we know about lists and function declarations and implementations, let's mix all of that for quintessential learning value:

We are going to write a function which takes an array of Float and calculates their power of x. Let's create a new file power.hs:

-- This is our function from fn.hs which we just copy/pasted here
powerOfX :: Float -> Float -> Float
powerOfX x y = x ** y

-- This is our new function which goes through a list
powerOfXForList :: [Float] -> Float -> [Float]
powerOfXForList list y = [powerOfX x y | x <- list]

Let's explain powerOfXForList's definition here: we take as an input 2 parameters:

  • list which is of type [Float] (this is our list of floats which we want to take to the power of something)
  • y which is of type Float (this is our power of something)

And we are going to return 1 thing [Float] which is a new list identical to the first one, except every element will have been calculated to the power of something.

Then we load it in ghci using :l <filename> as we saw in the first article:

*Main> :l power.hs
[1 of 1] Compiling Main             ( power.hs, interpreted )
Ok, one module loaded.

Our functions are loaded in the environment, we can play with it a bit:

*Main> a = [1,2,3,4,5]
*Main> powerOfXForList a 2
[1.0,4.0,9.0,16.0,25.0]
*Main> powerOfXForList a 3
[1.0,8.0,27.0,64.0,125.0]

Everything looks good, we're done for now, let's take a break before the next article.

Conclusion

Two Down, around eight more articles to go. I do hope that this post instilled some curiosity even maybe a pet project in Haskell. In the next article we will discuss infix vs prefix functions and discover more about types.

PS: Part 3 can be found here.

If you have a problem and no one else can help. Maybe you can hire the Kalvad-Team.