The blog post introduces lists and tuples, which are all important concepts in Haskell.

Lists
Lists, as you might know already, are an invaluable data structure that get used extensively across various programming languages, and Haskell is no exception. In Haskell, a list can only store one type.
[1,2,3,4,5]::[Integer]
There are two list constructors in Haskell:
- Empty list:
[]
- Pre-pended list:
x:xs
Using these list constructors, we can create [1,2,3,4,5]
programmatically like the following:
1:2:3:4:5:[]
This will create [] -> [5] -> [4,5] -> [3,4,5] -> [2,3,4,5] -> [1,2,3,4,5]
.
Functions for Lists
As we use lists extensively, Haskell has a set of useful predefined functions for lists.
import Data.List
--- This is where the predifined functions live
--- When trying the functions below, you simply need to import the above
--- Get the first element
head :: [a] -> a
head [1,2,3,4,5] --- Output: 1
--- Remove the first element
tail :: [a] -> [a]
tail [1,2,3,4,5] --- Output: [2,3,4,5]
--- Get the length
length :: [a] -> Integer
length [1,2,3,4,5] --- Output: 5
--- Remove the last element
init :: [a] -> [a]
init [1,2,3,4,5] --- Output: [1,2,3,4]
--- Determine if list is empty
null :: [a] -> Bool
null [] --- Output: True
null [1,2,3,4,5] --- Output: False
Functions for List of Booleans
There are some situations where you need to perform boolean logic on a list of booleans (e.g., checking if all the conditions are met). In such situations, the following functions will be helpful.
--- AND operator
and :: [a] -> Bool
and [True, False, True] --- Output: False
--- OR operator
or :: [a] -> Bool
or [True, False, True] --- Output: True
Now that we know the basic functions for lists, let's see how we can perform operations on lists.
List Comprehensions
We can use the following syntax for list comprehensions.
[<gen> | <elem> <- <list>, ..., <guard> , ...]
--- <gen> : operation on element(s) if guard(s) is/are met
--- <elem>: each element of a list
--- <list>: list
--- <guard>: guard condition (if you are not sure, check out Road to Haskeller #2)
--- Examples
[2*x | x <- [1,2,3]] --- Output: [2,4,6]
[2*x | x <- [1,2,3], x > 1] --- Output: [4,6]
[(x,y) | x <- [1,2,3], y <- [a,b], x>1]
--- Output: [(2, a), (2, b), (3, a), (3, b)]
In the above, we can use as many lists and guards as possible.
List Patterns
In the previous article of Road to Haskeller (#2), we discussed pattern matching. Using this concept, we can define functions on lists very concisely.
--- Take sum of all the elements
sum :: [a] -> a
sum [] = 0
sum (x:xs) = x + sum xs
--- we are decomposing list and recursively adding elements
-- Extract even numbers
even :: [a] -> [a]
even [] = []
even (x:xs) =
| mod x 2 == 0 = x:even(xs) --- mod calculates reminder
| otherwise = even(xs) --- do not prepend odd numbers
Tuples
Tuples are also an essential data type in Haskell, which can be defined as follows:
(1, "Hello") :: (Integer, String)
(1, 2) :: (Integer, Integer)
Unlike lists, tuples can have one or more types inside.
Functions for Tuples
Like lists, there are pre-defined functions for tuples as well.
--- Get the first element
fst:: (a, b) -> a
fst (x, _) = x
fst (1,2) --- Output: 1
--- Get the last element
snd:: (a, b) -> b
snd (_, y) = y
snd (1,2) --- Output: 2
That's it for today. Make sure you understand these data types because they are super important!
Exercises
This is an exercise section where you can test your understanding of the material introduced in the article. I highly recommend solving these questions by yourself after reading the main part of the article. You can click on each question to see its answer.
Resources
- Philipp, Hagenlocher. 2020. Haskell for Imperative Programmers #4 - Lists and Tuples. YouTube.
- Philipp, Hagenlocher. 2020. Haskell for Imperative Programmers #5 - Lists Exercises. YouTube.