# Lists

Append lists

> [1,2,3] ++ [7,8,9]
[1,2,3,7,8,9]
> "Hello" ++ " " ++ "Haskell!"
> ['h','e'] ++ ['y','a']
"heya"

Prepend to list

> 4:[1,2,3]
[4,1,2,3]
> 1:2:3:[]
[1,2,3]

Note: Prepending much more performant on large lists.

Index a list

> [1,2,3] ! 0
1
> [1,2,3] ! 2
3
> [1,2,3] ! 3
Exeption

Core functions

Function Description
head Return the first element in a list.
tail Return everything except the head.
last Return the last element in a list.
init Return everything except the last element.
length Return the length of a list.
null Check if a list is empty.
reverse Reverse a list.
take Take n elements from beginning of list.
drop Drop n elements from beginning of list.
maximum Return the biggest element in a list.
minimum Return the smallest element in a list.
sum Return the sum of a list of numbers.
product Return the product of a list of numbers.
elem Check if n exists in a list.

Ranges

> [1..5]
[1,2,3,4,5]
> ['a'..'e']
['a','b','c','d','e']
> [2,4..10]
[2,4,6,8,10]

List Comprehensions

Ranges are easy and very useful. However, if you want to construct a list from a more complicated pattern than linear growth, you will need to use something else. Haskell provides List Comprehensions for this purpose.

> [ x*x | x <- [1..5] ]
[1,4,9,16,25]

You can also pass in multiple lists.

> [ [x,y] | x <- [1,2,3], y <- [7,8,9] ]
[[1,7],[1,8],[1,9],[2,7],[2,8],[2,9],[3,7],[3,8],[3,9]]

Conditions

You can add any number of conditions to a list comprehesions.

> [ x*x | x <- [1..10], x >= 50 ]
[64,81,100]
> [ x | x <- [50..100], x ‘mod‘ 7 == 3]
[52 ,59 ,66 ,73 ,80 ,87 ,94]
> [ x | x <- [1..5], x /= 2, x /= 4 ]
[1,3,5]

# Types

Haskell is a type-safe language. This makes it possible for GHC to catch many errors at compile time, making your (compilable) code much more reliable. However, unlike other type-safe languages such as Java, Haskell implements type inference. In other words, Haskell will infer what a given type should be without you having to state it explicitly.

Everything in Haskell has a type. To view the type, you can use the :t command.

> :t ’a’
’a’ :: Char
> :t True
True :: Bool
> :t "HELLO!"
"HELLO!" :: [Char]
> :t (True, ’a’)
(True, ’a’) :: (Bool, Char)
> :t 4 == 5
4 == 5 :: Bool
Type Description
Int Bounded integer like in C
Integer Unbounded integer. Can be inefficient.
Float Single precision floating point integer.
Double Double precision floating point integer.
Bool True or false.
Char Single character.

Type Definition

Although not necessary, it’s good practice to supply a type declaration before your function.

<func> :: <:t param> -> <:t retval>
removeNonUppercase :: [Char] => [Char]
removeNonUppercase xs = [ x | x <- xs, x 'elem' ['A'..'Z'] ]

If your function takes multiple parameters, separate those with -> as well. The last in the chain is always the return value.

<func> :: <:t p1> -> <:t p2> -> ... -> <:t retval>
> :t take
take :: Int -> [a] -> [a]

Some functions can accept parameters of any type. These are called type variables and are typically denoted by a,b,c,d,…

> :t fst
fst :: (a, b) -> a

# Typeclasses

Types can be grouped as Typeclasses. If a type is member of a certain typeclass, that means it supports certain behavior that the typeclass implements. You can impose type contraints in your type definitions using the => symbol.

> :t (==)
(==) :: Eq a => a -> a -> Bool

This means that the == function accepts two variables of the same arbitrary type and retuns a boolean. However, that type must be a member of the Eq typeclass. The Eq typeclass allows Haskell to test for equality.

Typeclass Description
Eq Supports equality testing (== and /=).
Ord Supports ordering (>, <, >=, <=).
Show Supports conversion to a string.
Read Supports converting from a string.
Enum Supports sequential ordering.
Bounded Has an upper and lower bound.
Num Supports numberic functions (+, -, *, /).
Integral Includes only whole numbers (Int, Integer).
Floating Includes only floating point numbers (Float, Double).

# Functions

Pattern Mathing

Haskell allows you to create multiple function definitions. This is useful for handling error cases and corner cases.

factorial :: (Integral a) => a -> a
factorial 0 = 1
factorial n = n * factorial (n - 1)
length' :: (Num b) => [a] -> b
length' [] = 0
length' (_:xs) = 1 + length' xs
sum' :: (Num a) => [a] -> a
sum' [] = 0
sum' (x:xs) = x + sum' xs

As Patterns

As patterns are a handy way to pattern match without destructuring the original input. You use an @ symbol for this.

firstLetter :: String -> String
firstLetter "" = "The string is empty"
firstLetter all@(x:xs) = "The first letter of " ++ all ++ " is " ++ [x]

Guards

Guards are similar to if statements. They allow you to apply conditional logic to patterns. You put a | in front of each guard.

max' :: (Ord a) => a -> a -> a
max' a b
| a > b      = a
| otherwise  = b
compare :: (Ord a) => a -> a -> Ordering
a 'compare' b
| a > b   = GT
| a == b  = EQ
| b < a   = LT

Where Binding

initials :: String -> String -> String
initials firstName lastName = [f] ++ [l]
where (f:_) = firstName
(l:_) = lastName
initials firstName middleName lastName = [f] ++ [m] ++ [l]
where (f:_) = firstName
(m:_) = middleName
(l:_) = lastName
calcBmis :: (RealFloat a) => [(a, a)] -> [a]
calcBmis xs = [bmi w h | (w, h) <- xs]
where bmi weight height = weight / height ^ 2

Let Binding

Another useful construct is the let binding. Unlike the where binding which is just a syntactic construct, the let bunding is an expression. This makes it much more versitile. You can use let just about anywhere.

cylinderArea :: (RealFloat a) => a -> a -> a
cylinderArea r h =
let sideArea = 2 * pi * r * h
topArea = pi * r ^ 2
in  sideArea + 2 * topArea
> 4 * (let a = 99 in a + 1) + 20
420
calcBmis :: (RealFloat a) => [(a, a)] -> [a]
calcBmis xs = [ bmi | (w, h) <- xs, let bmi = w / h ^ 2 ]

Here’s an implementation of quicksort.

quicksort :: (Ord a) => [a] -> [a]
quicksort [] = []
quicksort (x:xs) =
let smallSorted = quicksort [ a | a <- xs, a <= x ]
bigSorted = quicksort [ a | a <- xs, a > x ]
in smallSorted ++ [x] ++ bigSorted