Note that in the above example, we had to provide a more exhaustive pattern match to handle cases where an odd length list is given as an argument.
The Haskell Prelude defines many built-ins for handling lists, like map, filter, etc.. Where possible, you should use these instead of writing your own recursive functions.
List basics
The type constructor for lists in the Haskell Prelude is []. The type declaration for a list holding values of type Int is written as follows:
xs::[Int]-- or equivalently, but less conveniently,xs::[]Int
Lists in Haskell are homogeneous sequences(opens new window), which is to say that all elements must be of the same type. Unlike tuples, list type is not affected by length:
`(:)`, pronounced "cons", prepends elements to a list. Consing `x` (a value of type `a`) onto `xs` (a list of values of the same type `a`) creates a new list, whose **head** (the first element) is `x`, and **tail** (the rest of the elements) is `xs`.
We can define simple lists as follows:
ys::[a]ys=[]xs::[Int]xs=12:(99:(37:[]))-- or = 12 : 99 : 37 : [] -- ((:) is right-associative)-- or = [12, 99, 37] -- (syntactic sugar for lists)
Note that (++), which can be used to build lists is defined recursively in terms of (:) and [].
Ranges
Creating a list from 1 to 10 is simple using range notation:
[1..10]-- [1,2,3,4,5,6,7,8,9,10]
To specify a step, add a comma and the next element after the start element:
[1,3..10]-- [1,3,5,7,9]
Note that Haskell always takes the step as the arithmetic difference between terms, and that you cannot specify more than the first two elements and the upper bound:
[1,3,5..10]-- error[1,3,9..20]-- error
To generate a range in descending order, always specify the negative step:
[5..1]-- [][5,4..1]-- [5,4,3,2,1]
Because Haskell is non-strict, the elements of the list are evaluated only if they are needed, which allows us to use infinite lists. [1..] is an infinite list starting from 1. This list can be bound to a variable or passed as a function argument:
take5[1..]-- returns [1,2,3,4,5] even though [1..] is infinite
Be careful when using ranges with floating-point values, because it accepts spill-overs up to half-delta, to fend off rounding issues:
[1.0,1.5..2.4]-- [1.0,1.5,2.0,2.5] , though 2.5 > 2.4[1.0,1.1..1.2]-- [1.0,1.1,1.2000000000000002] , though 1.2000000000000002 > 1.2
Ranges work not just with numbers but with any type that implements Enum typeclass. Given some enumerable variables a, b, c, the range syntax is equivalent to calling these Enum methods:
When implemented as singly-linked lists, these operations take O(n) time. If you frequently access elements by index, it's probably better to use Data.Vector (from the vector(opens new window) package) or other data structures.
The last line is equivalent to ((0 + 1) + 2) + 3. This is because (f a b) is the same as (afb) in general, and so ((+) 0 1) is the same as (0 + 1) in particular.
foldr
This is how the right fold is implemented:
foldr::(a->b->b)->b->[a]->bfoldrfz[]=zfoldrfz(x:xs)=fx(foldrfzxs)-- = x `f` foldr f z xs
The right fold, foldr, associates to the right. That is:
foldr(+)0[1,2,3]-- is equivalent to 1 + (2 + (3 + 0))
The reason is that foldr is evaluated like this (look at the inductive step of foldr):
The last line is equivalent to 1 + (2 + (3 + 0)), because ((+) 3 0) is the same as (3 + 0).
Transforming with map
Often we wish to convert, or transform the contents of a collection (a list, or something traversable). In Haskell we use map:
-- Simple add 1
map (+ 1) [1,2,3]
[2,3,4]
map odd [1,2,3]
[True,False,True]
data Gender = Male | Female deriving Show
data Person = Person String Gender Int deriving Show
-- Extract just the age from a list of people
map (\(Person n g a) -> a) [(Person "Alex" Male 31),(Person "Ellie" Female 29)]
[31,29]
Filtering with filter
Given a list:
li = [1,2,3,4,5]
we can filter a list with a predicate using filter :: (a -> Bool) -> [a] -> [a]:
filter (== 1) li -- [1]
filter (even) li -- [2,4]
filter (odd) li -- [1,3,5]
-- Something slightly more complicated
comfy i = notTooLarge && isEven
where
notTooLarge = (i + 1) < 5
isEven = even i
filter comfy li -- [2]
Of course it's not just about numbers:
data Gender = Male | Female deriving Show
data Person = Person String Gender Int deriving Show
onlyLadies :: [Person] -> Person
onlyLadies x = filter isFemale x
where
isFemale (Person _ Female _) = True
isFemale _ = False
onlyLadies [(Person "Alex" Male 31),(Person "Ellie" Female 29)]
-- [Person "Ellie" Female 29]
Zipping and Unzipping Lists
zip takes two lists and returns a list of corresponding pairs:
non-empty list constructor
`(:) :: a -> [a] -> [a]`
head - returns the first value of a list
`head :: [a] -> a`
last - returns the last value of a list
`last :: [a] -> a`
tail - returns a list without the first item
`tail :: [a] -> [a]`
init - returns a list without the last item
`init :: [a] -> [a]`
xs !! i - return the element at an index i in list xs
`(!!) :: Int -> [a] -> a`
take n xs - return new list containing n first elements of the list xs
`take :: Int -> [a] -> [a]`
map :: (a -> b) -> [a] -> [b]
filter :: (a -> Bool) -> [a] -> [a]
(++) :: [a] -> [a]
concat :: [[a]] -> [a]
Remarks
The type [a] is equivalent to [] a.
[] constructs the empty list.
[] in a function definition LHS, e.g. f [] = ..., is the empty list pattern.
x:xs constructs a list where an element x is prepended to the list xs
f (x:xs) = ... is a pattern match for a non-empty list where x is the head and xs is the tail.
f (a:b:cs) = ... and f (a:(b:cs)) = ... are the same. They are a pattern match for a list of at least two elements where the first element is a, the second element is b, and the rest of the list is cs.
f ((a:as):bs) = ... is NOT the same as f (a:(as:bs)) = .... The former is a pattern match for a non-empty list of lists, where a is the head of the head, as is the tail of the head, and bs is the tail.
f (x:[]) = ... and f [x] = ... are the same. They are a pattern match for a list of exactly one element.
f (a:b:[]) = ... and f [a,b] = ... are the same. They are a pattern match for a list of exactly two elements.
f [a:b] = ... is a pattern match for a list of exactly one element where the element is also a list. a is the head of the element and b is the tail of the element.
[a,b,c] is the same as (a:b:c:[]). Standard list notation is just syntactic sugar for the (:) and [] constructors.
You can use all@(x:y:ys) in order to refer to the whole list as all (or any other name you choose) instead of repeating (x:y:ys) again.