You are on page 1of 24

Hutton 2 Notes

Foreword: Meijer
mainstreaming of FP watered down foundational principles
The true value of FP lies in leveraging first class function to achieve composit
ionality and Equational Reasoning.
"Functional Programming is a style of programming where the basic model of comp
utation is the application of functions to arguments"
Hutton uses FP as a tool for thought, focusing on an elegant and concise express
ion of intent, makes the case that a pure and lazy FP language is a good medium
to reason about algorithms at a high level of abstraction.
Toc
Part 1
Chapter
Chapter
Chapter
Chapter
Chapter

1:
2:
3:
4:
5:

Introduction
First Steps
Types And Classes
Defining Functions
List Comprehensions

Chapter
Chapter
Chapter
Chapter

6:
7:
8:
9:

Recursive Functions
Higher Order Functions
Declaring Types and Classes
The Countdown Problem

Part 2:
Chapter
Chapter
Chapter
Chapter
Chapter
Chapter
Chapter
Chapter

10:
11:
12:
13:
14:
15:
16:
17:

Interactive Programming
Unbeatable Tic Tac Toe
Monads and More
Monadic Parsing
Foldables and Friends
Lazy Evaluation
Reasoning about programs
Calculating Compilers

Chapter 1: Introduction.
Notion of a function, notion of FP, main features and history of Haskell, 3 exa
mples.
1.1 Function
A function is a mapping that takes one or more arguments and produces a single r
esult.
A function is defined in Haskell by an equation that gives a name to the functio
n, its arguments, and a body that specifies how the result can be calculated in
terms of its arguments.
triple x = x + x + x
When a function is applied to actual arguments, the arguments are substituted in
to the body in place of argument names.
This process may yield an expression that cannot be evaluated further, or it may
need more evaluation by evaulating inner functions.
Two alternate ways of evaluating 'double (double 2)'
Key principle: The order of applying functions in a calculation does not affect
the value of the final result, but it can affect the number of steps required an
d whether the calculation terminates.
1.2 Functional Programming

Functional Programming can be considered a style of programming where the basic


method of computation is applying a function to arguments. In turn, an FP langua
ge is one that supports and encourages the functional style.
Task: Compute sum of integers between 1 and some number n >= 1.
In Java
int total = 0;
for (int i = 0, count = 1; count <= 1; count++){
total += 1;
}
basic concept = use two integer variables whose values can be mutated over time
using the assignment operator =.
one variable is used to accumulate the total
the other is used to count from 1 to n
iow, we initialize the 'total' variable to 0. Then enter a loop that ranges the
variable 'count' from 1 to n, adding the current value of count to total every t
ime through the loop.
the basic method of computing (in imperative languages) is to *change stored val
ues*, in the sense that executing the program results in a sequence of assignmen
ts.
e.g: n = 5
total = 0
count = 1
total = 1
count = 2
total = 3
count = 3
total = 6
count = 4
total = 10
count = 5
total = 15
Imperative languages are constructed from imperative instructions that specify p
recisely how computations should proceed.
In Haskell
sum [1..n]
Here the basic method is applying functions to arguments.
iow executing the program results in a *sequence* of *applications*
e.g for n = 5
sum [1..n]
=
applying ..
sum [1,2,3,4,5]
=
applying sum

1 + 2 + 3 + 4 + 5
=
applying +
15
Most imperative languages offer some support for functions so the Haskell progra
m sum 1..n can be translated into such languages, but they don't encourage such
a style. e.g they might not allow functions to be stored in data structures or p
assed to other functions as arguments, or be returned as results.
1.3 Features of Haskell
1. Concise Programs. (Chapters 2,4)
- programs written in functional styles are often very concise
- syntax has been designed to be concise
2. Powerful Type System (Chapters 3, 8)
- type inference eliminates large classes of errors
- supports very general forms of polymorphism and overloading
- provides a wide range of special features concerning types
3. List Comprehensions (chapter 5)
- lists of values are a common way to structure and manipulate d
ata in computing
- Haskell provides lists as basic data structure + powerful comp
rehension notation
- this allows many common ops on lists to be defined clearly and
concisely, without explicit recursion
4. Recursive Functions (Chapter 6)
- the basic mechanism for 'looping'
- pattern matching and guards separate different cases into diff
erent equations
- many patterns of computation are naturally expressed as recurs
ive functions
5. Higher Order Functions (Chapter 7)
- functions can be arguments to other functions and returned as
results
- allows common programming patterns to be defined as functions
(e.g composition)
- can be used to define domain specific languages (e.g for list
processing, interactive programs,parsing)
6. Effectful functions (chapters 10, 12)
- Haskell functions are *pure* = taking all inputs as argumens a
nd producing outputs as results
- this would seem to be at odds when you need side effects (e.g
i/o)
- Haskell provides a uniform framework for programming with such
effects without compromising purity, based on monads and applicatives
7. Generic Functions (Chapters 12, 14)
- Most languages allow functions that are generic over simple ty
pes e.g different forms of numbers
- Haskell supports functions that are generic over richer struct
ures e.g: functorial, applicative, monadic, foldable, and traversable
- allows new structures and functions generic over these structu
res to be defined
8. Lazy Evaluation
- Haskell programs are evaluated using lazy evaluation
- LE is based on the idea that no computations should be perform
ed until the result is actually required
- avoids unnecessary computation
- programs terminate whenever possible
- encourages programming in a modular style using intermediate d
atastructures
- allows programming with infinite structures

9. Equational Reasoning (Chapters 16, 17)


- since programs in Haskell are pure functions, equational reaso
ning techniques can be used to
- execute programs
- transform programs
- prove properties of programs
- calculate properties directly from specifications of t
heir behaviour
- equational reasoning is particularly powerful when combined wi
th the use of induction to reason about functions that are defined using recursi
on
1.4 Historical Background
- 1930 Alonzo Church develops lambda calculus
- 1950 John McCarthy develops Lisp
- 1960 Peter Landin develops ISWIM, first pure FP language
- 1970 John Backus develops FP, an FP that emphasised HoFs, reas
oning
- 1970 Robin Milner et al develop ML introduced polymorphic func
tion types and inference
- late 1970/early 1980 - David Turner develops Miranda - lazy fu
nctional PL
- 1997 Haskell development initiated
- 1980 Wadler et al develop type classes to support overloading,
monads to handle effects
- 2003 Haskell Report published, defines stable version
- 2010 Revised and Updated Haskell version published
1.5 Taste of Haskell
nothing very interesting except the seqn function's signature
Chapter 2
- GHC system
- standard prelude
- notation for function application
- develop first Haskell script
- a number of syntactic conventions concerning scripts
functional application is denoted using spacing
the mathematical f(a,b) + c d ;; note that in math space op = multiply is the Ha
skell f a b + c * d
Functional application has the highest priority of all operators in the language
.
:r reload
:l load
:set editor name -> sets editor to name
:t expr -> gives type of expression
? shows all commands
function names must begin with lower case
reserved words
1.
2.
3.
4.
5.

case
class
data
default
deriving

6. do

7. else
8. foreign
9. if
10. import
11.
12.
13.
14.
15.

in
infix
infixl
infixr
instance

16.
17.
18.
19.
20

let
module
newtype
of
then

21 type
22 where
layout rule
Within a script each definition at the same level must begin precisely at the sa
me column
This makes it possible to define the groupings of definitions from their indenta
tion
e.g
a = b + c
where
b = 1
c = 2
d = a * 2
Explicit parens + semi colon maybe used to group a sequence of definitions
e.g
a = b + c
where
(b = 1;
c = 2);
d = a * 2
or even
a = b + c where ( b = 1; c = 2); d = a * 2
Generally better to follow layout rule
Tabs can create problems so set editor to autoconvert to spaces
-- This is a non nested comment
{- This is a {-nested comment -}
and multiline too
-}
Chapter 3: Types and Classes
Types and classes are two very fundamental concepts in Haskell.

What are types? How are they used?


basic types and how to combine them
function types in detail
concepts of polymorphism and type classes
3.1 Basic Concepts
a type is a collection (set?) of related values.
e.g: The type Bool contains the two values True and False
e.g: The type Bool -> Bool has as values all functions that take a boolean argum
ent and return a boolean. for example, the function not
Notation - v :: T = v is a value in type T = value v has type T
e.g: False :: Bool
not :: Bool -> Bool
The symbol :: can also be used with values that have not been evaluated in which
case e :: T is interpreted as 'when e is evaluated a value v of type T results'
e.g: not False :: Bool
not True :: Bool
not (not False) :: Bool
In Haskell, every expression must have a type, which is evaluated before the exp
ression is evaluated. The calculation of the type is called 'type inference'
Typing rule for function application. f :: A -> B, e :: A / f e :: B
Expressions such as 'not 3' which do not have a type/for which a type cannot be
inferred are said to contain a 'type error' and are deemed to be invalid express
ions.
Because type inference preceeds evaluation, Haskell programs are 'type safe' ==
type errors can't happen during evaluation, and this eradicates large classes of
errors.
however 1 `div` 0 will still evaluate to an error though it is well typed.
:t 1 `div` 0
1 `div` 0 :: Integral a => a

3.2 Basic Types


provided by Haskell
- Bool: logical values. True, False
- Char: single characters.
contains all single characters in the Unicode system, the intern
ational standard for representing text based information,including all normal En
glish letters, a number of control characters (e.g '\n').
- String: strings of characters
- "abc", "" (the empty string)
- Int: fixed precision integers. For GHC range -2^63 to 2^63 -1. Going o
utside this range gives unexpected results.
- Integer: arbitrary precision integers
- integers with no upper or lower limits but memory of the syste
m
- Float: single precision floating point numbers

- Double: Double precision floating point numbers


A single number may have more than one numeric type.
e.g 3 can have type Int, Integer, Float, or double.
This raises the question of what type these numbers should be assigned during ty
pe inference. This is dealt with later, when we consider type classes.
3.3 List Types
A list is a sequence of elements of the same type
e.g: [False,True,False] :: [Bool]
length of a list = number of elements in the list
*Main> length [1,2,3]
3
Emptylist [] :: [t]
The type of a list conveys no information about its length
There is no restriction about the type of elements in a list. We can have lists
of lists, lists of functions, lists of lists of functions etc
There is no restriction that a list should have a finite length. With the use of
lazy evaluation, lists of inifinite lengths are natural and practical
3.4 Tuple Types
A tuple is a *finite* sequence of components of *possibly different types*, with
the components being enclosed in round parentheses, and separated by commas.
For tuples with i-th components having type T_i, we write the type as (T_1, T_2,
T_3 ... )
so (False,True) :: (Bool,Bool)
(False, 'a',True) :: (Bool, Char, Bool) etc
the number of components in a tuple is called its arity.
Empty tuple () :: ()
Tuples of arity 1 such as (False) are not permitted.
The type (Bool, Char) contains all pairs (tuples of arity 2) with a first compon
ent of type Bool and a second component of type Char
There are no restrictions on the type of tuples. We can have tuples of lists of
functions etc
Tuples must have finite arity. This ensures that tuple types can be inferred pri
or to evaluation.

3.5 Function Types


A function is a mapping from arguments of one type to the result of another type
.
T1 -> T2 is the type of all functions that take arguments of type T1 and return
results of type T2
e.g: not :: Bool -> Bool
(key insight) since there are no restrictions on the types of the arguments and
the results of a function, the notion of a function with a single argument and a
single result is sufficient to handle functions with multiple arguments and res

ults, by packaging multiple values into lists or tuples.


e.g: a function add that adds up a pair of integers
add :: (Int, Int) -> Int
add (x,y) = x + y
e.g: a function that returns a list of numbers from 0 to n
zeroTo :: Int -> [Int]
zeroTo n = [0..n]
There is no restriction that functions must be total. There maybe some arguments
for which the result is not defined.
> head []
** Exception blah blah ..**
3.6 Curried Functions
Functions with multiple arguments can be handled in another way.
Consider
add' :: Int -> (Int -> Int)
add' x y = x + y
The type states that add' is a function that takes an integer and returns a *fun
ction* that takes an integer and returns an integer
The definition states that add' takes an integer x and returns a function that i
n turn takes an integer y and returns x + y.
Note: add produces the same result as add'. But add takes two arguments at the s
ame time, packaged as a pair, where add' takes two arguments, one at a time. The
types reflect this.
Functions like add' are called curried functions. They are more flexible than fu
nction on tuples, because
(key point) useful functions can often be made by ***partially applying*** curri
ed functions with less than a full complement of arguments.
e.g a function that increments its argument by one can be created by add' 1
Some conventions and associations.
The 'function arrow' in types associates to the right
so
Int -> Int -> Int is really Int -> (Int -> Int)
Consequently function application associates to the left
mult x y z is really (((mult x) y) z)
Unless tupling is explicitly required, multi argument functions in Haskell are c
urried.
Later we'll see how curried functions can be formalized using lambda expressions
.
3.7 Polymorphic Types
The library function length calculates the length of any list irrespective of co
ntent type.

*Main> :t length
length :: Foldable t => t a -> Int
The idea that length can work with lists whose elements have any type is made pr
ecise in its type declaration by using a type variable
length :: [a] -> int
i.e for any type a, (a is a type variable) length has type [a] -> Int
The values of this variable are types like Int, Bool, Bool->Bool or whatever.
A type that contains one or more type variables is polymorphic.
So [a] -> Int is a polymorphic type and length is a polymorphic function.
Many of the functions provided in the standard prelude are polymorphic.
Eg.
head :: [a] -> Int
take :: Int -> [a] -> [a]
zip :: [a] -> [b] -> [(a,b)]
id :: a -> a
3.8 Overloaded Types
The arithmetic operator + calculates the sum of any two numbers of the same nume
ric type.
It can be used to calculate the sum of two integers, or the sum of two floating
point numbers.
> 1 + 2 => 3
> 1.0 + 2.0 => 3.0
(key) the idea that + can be applied to numbers of any type is made clear by usi
ng a class constraint.
Class constraints are written in the form C a where C is a class and a is a type
variable
For example the type of the addition operator + is as follows
(+) :: Num a => a -> a -> a
That is for any type a that is an *instance* of the (type)class Num, the functio
n + has the type
a -> a -> a
(Parethesizing an operator converts it into a curried function, as we'll see in
Chapter 6
A type that contains one or more class constraints is called an overloaded type.
A function with a type signature that contains a class constraint, i.e has an ov
erloaded type , is an overloaded function.
e.g:
(+) :: Num a => a -> a -> a
negate :: Num a => a -> a -> a
Numbers themselves are overloaded
3 :: Num a => a means that for any numeric type a, 3 has type a
in this manner, 3 could be an integer, a floating point number, or more generall
y a value of any numeric type depending on the context of use

3.9 Basic Classes


A type is a collection of related values
A class is a collection of types that support certain overloaded operations call
ed *methods*
Haskell provides a number of built in types of which the most common are describ
ed belowe. (More advanced built in classes are considered in part 2)
1. Eq - Equality Types
This class contains types whose values can be compared for equal
ity and inequality using the following two methods
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
All basic types Bool, Char, String, Int, Integer, Float and Doub
le are instances of the Eq class as are list and tuple types *provided* that the
ir element and component types are instances.
E.g: False == False => True
'a' == 'b' => False
"abc" == "abc" => True
[1,2)] == [1,2,3] => False
('a',False) == ('a',False) =. True
Note: Function types are in general *not* members of the Eq class because in gen
eral function(value)S cannot be compared for equality.
2. Ord - Ordered types
instances of the typeclass Eq but in addition whose values are l
inearly ordered, and can be compared using the following six methods
(<) :: a -> a -> Bool
(<=) :: a -> a -> Bool
(>) :: a -> a -> Bool
(>=) :: a -> a -> Bool
min :: a -> a -> Bool
max :: a -> a -> Bool
All basic types - Int, Integer, Bool, Char, String, Float, and D
ouble are instances of the Ord typeclass, as are list and tuple types as long as
their element/component types are instances .
e.g:
False < Ture => True
min 'a' 'b' => 'a'
"elegant" < "elephant" => True
[1,2,3] < [1,2] => False
('a',2) < ('b', 1) => True
Strings, lists and tuples are ordered lexicographically
3. Show - showable types
This class contains types whose values can be converted into str
ings using the following methoh
show :: a -> String
All basic types - Int, Integer, Float, Double, Char, String, are instances of th
e Show class, as are list and tuple types provided their element/component types
are instances.

e.g: show False => "False"


4. Read - readable types
this class is dual to show, and contains types whose values can
be converted from strings of characters using the following method
read :: String -> a
all basic types are instances of this typeclass as are list and tuple types prov
ided their element/component types are instances.
>
>
>
>
>

read
read
read
read
read

"False" :: Bool => Bool


'a' :: Char => 'a'
"123" :: Int => 123
"[1,2,3]" :: [Int] => [1,2,3]
"('a', False)" :: (Char, Bool) => ('a',False)

the use of :: in these examples resolves type of the result. Most times this is
not required.
The result of read is undefined if its argument is not syntactically valid.
5 Num - numeric types
types whose values are numeric and such can be processed with th
e following 6(5?) methods
(+) :: a -> a -> a
(-) :: a -> a -> a
(*) :: a -> a -> a
negate :: a -> a -> a
abs :: a -> a -> a
signum :: a -> a -> a
The basic types Int, Integer, Float, Double, are instances of th
e Num class.
The Num class does not provide a division operator, but division is hand
led separately using two special typeclasses one for integral numbers and one fo
r fractional numbers
6. Integral -integral types
contains values which are instances of types contained in class
Num, but inaddition whose values are integers, and as such support methods of in
teger divison and reminder
div :: a -> a -> a
mod :: a -> a -> a
in practice these methods are often used as infix operators thus
7 `div` 2 => 3
7 `mod` 2 => 1
For efficiency reasons, a number of prelude functions that involve both lists an
d integers are restricted to type Int of finite precision integers, rather than
being applicable to any type of the Integral type class.
If required, generic versions of these functions are provided as part of an addi
tional library file called Data.List
7. Fractional - fractional types
This class contains types that are instances of Num butwhose val
ues are non-integral. and support methods of fractional divison and reciprocatio
n

(/) :: a -> a -> a


recip :: a -> a
e.g
7.0 / 2.0 => 3.5
recip 2.0 => 0.5

Chapter 4: Defining Functions


a range of mechanisms for defining functions
4.1 New from Old
- create new functions by combining existing ones
e.g:
even :: Integral a => a -> a -> a
even n = n `mod` 2 == 0
recip :: Fractional a => a -> a
recip n = 1 / n
etc.
4.2 Conditional Expressions
Haskell provides a number of different ways to define functions that choose betw
een a number of possible results.
Simplest such = conditional expressions which use a logical expression, a condit
ion, to choose between two values of the same type.
abs :: Int -> Int
abs n = if n >= 0 then n else -n
Cond exps can be nested. Must have both then and else branches
4.3 Guarded Equations
Alternative to conditional expressions
A sequence of logical expressions, guards, is used to choose between a sequence
of results of the same type.
If first guard is true, then the first result is chosen. If second, second. etc
abs n | n >= 0 = n
| otherwise = -n
Main advantage over conditional expressions == easier to read.
4.4 Pattern Matching
A sequence of syntactic expressions called patterns i s used to choose between
a sequence of results of the same type. If the first pattern is matched, then th
e first result is chosen and so on.
e.g:
not :: Bool -> Bool
not False = True
not True = False
Functions with more than one argument can be defined using pattern matching, in

which case patterns for each argument are matched in order within each equation.
e.g:
(&&) :: Bool -> Bool -> Bool
True && True = True
True && False = False
False && True = False
False && False = False
Using the wild card pattern this becomes
(&&) :: Bool -> Bool -> Bool
True && True = True
_ && _ = False
Haskell does not permit the use of the same name for different arguments in the
same equation.
Basic patterns = variables, values, wildcard pattern
2 useful ways to build larger patterns from more complex ones
Tuple Patterns
a tuple of pattern is itself a pattern
which matches any tuple of the same arity whose components all match the corresp
onding patterns in order
e.g:
fst :: (a,b) -> a
fst (x,_) = x
snd :: (a,b) -> b
snd (_,y) = y
List patterns
A list of patterns it itself a pattern which matches a list of the same length,
whose elements match the corresponding patterns in order
e.g:
test :: [Char] -> Bool
test ['a',_,_] = True
test _ = False
The list [1,2,3] is just an abbreviation for 1: ( 2 : ( 3 : []))
the cons operator assosciates to the righth so 1: ( 2 : ( 3 : [])) can be expres
sed as 1 : 2 : 3 : []
The cons operator can be used to pattern match *non empty* lists
more general (any length) lists starting with letter 'a'
test ['a':_] = True
test _ = False
head :: [a] -> a
head (x:_) = x
tail :: [a] -> [a]

tail (_: xs) = xs


The cons patters must be paranthesized because function application has the high
est priority
head x : _ = x is actually (head x) : _= x which is incorrect
4.5 Lambda Expressions
As an alternative to building functions with equations, we can build functions w
ith lambda expressions
a nameless function that doubles its arguments is \x -> x + x
These can be applied just like named functions
(\x -> x + x) 2 = 2
Lambda expressions can be used to formalize the meaning of curried expressions
The curried function
add :: Int -> Int -> Int
add x y = x + y
is the same as
add = \x -> (\y -> x + y)
Lambda expressions are useful when functions return values that are functions
e.g
const :: a -> b -> a
const x _ = x
it is more appealing to define const as function that returns a function as its
result
const :: a -> (b -> a)
const x = \_ -> x
lambda expressions can be used to avoid having to name a function that is only r
eferenced once in a program
For example a function odds that returns the first n odd numbers can be written
as
odds :: Int -> [Int]
odds n = map f [0 .. n-1]
where f x = x * 2 + 1
here the function has a name f. Which can be eliminated and the whole function s
implified by
odds n = map (\x -> x * 2 + 1) [0 .. n-1]
4.6 Operator Sections
Infix functions are called operators.
Any function with two arguments can be converted into an operator by enclosing i
t within two backquotes

so
2 `div`3 == div 2 3
Conversely any operator can be converted into a two arg function by enclosing it
s name in paretheses
thus (+) 1 2
if # is an operator expressions of the form (#) (x #) (# y) are called sections
.
Sections have 3 primary applications
The can be used to create functions in a compact way
(+) vs \x -> (\y -> (x + y))
(1+) the succesor fn vs \y -> 1 + y
(*2) the doubling function vs \x -> x * 2
sections are necessary when stating the type of operators because operators by t
hemselves are not valid Haskell expressions
e.g:
(+) :: Int -> Int -> Int
sections are necessary when using operators as args to other functions\
sum :: [Int] -> Int
sum = foldl (+) 0
4.7 Chapter Notes
4.8 Exercises
Chapter 5: List Comprehensions
5.1 Basic Concepts
> [x^2 | x <- [1..5]]
[1,4,9,16,25]
List of all possible pairings of elements of [1,2,3] with [4,5] is given by
[(x,y) | x <- [1,2,3], y <- [4,5]]
[(1,4),(1,5),(2,4),(2,5),(3,4),(3,5)]
Later generators can be considerd more deeply nested and so are exhausted once p
er earlier generator
Later generators can also depend on variables from earlier generators
>[(x,y)| x <- [1..3], y <- [x..3]]
[(1,1),(1,2),(1,3),(2,2),(2,3),(3,3)]
function concat that concatenates a list of lists can be defined by using one ge
nerator to select each list in turn and another generator to select elements fro
m the selected lists
concat :: [[a]] -> [a]
concat xss = [x | xs <- xss, x <- xs]
Wildcard pattern is useful in generators to discard specific elements

e.g a function that accumulates the first elements from a list of pairs
firsts :: [(a,b)] -> [a]
first ps = [x | (x,_) <- ps]
Similarly to calculate the length of a list, we can replace each element by 1 an
d sum the resulting list
length :: [a] -> Int
length xs = sum [1| _ <- xs]
where the generator _ <- xs simply serves as a counter to govern the production
of an appropriate number of 1s
5.2 Guards
List comprehensions can use logical expressions called guards to filter the valu
es produced by earlier generators. If a guard is True then the current values ar
e retained, if guard is False they are discarded
eg
>[x | x <- [1..10], even x]
[2,4,6,8,10]
> [x | x <- [1..10], even x, x < 7]
[2,4,6]
factors n = [x | x <- [1..n], x `mod` n == 0]
Suppose we represent a lookup table by a list of keys and values
Then for any type of keys that supports equality, a function find that returns t
he list of all values associated with a given key is given by
find :; Eq k => k -> [(k,v)] -> [v]
find k t = [v | (k',v ) <- t, k == k']
5.3 The zip function
The library function zip produces a new list by pairing elements from two existi
ng lists till one or both lists are exhausted.
Prelude> zip ['a','b','c'] [1,2,3,4]
[('a',1),('b',2),('c',3)]
zip is useful when programming with list comprehensions
a function that returns all pairs of adjacent elements
pairs :: [a] -> [(a,a)]
pairs xs = zip xs (tail xs)
5.4 String Comprehensions
Because strings are lists, list ops work on strings.
For the same reason, list comprehensions can be used to define functions on stri
ngs.
5.5 Caesar Cipher
An extended programming example
step 1. Encoding and decoding
- to get standard functions on characters
import Data.Char

- for simplicity only encode lowercase chars


- signatures
1. let2Int :: Char -> Int
2. int2Let :: Int -> Char
3. shift :: Int -> Char -> Char
4. encode :: Int -> String -> String
5. table :: [Float]
6. percent :: Int -> Int -> Float
7. freqs :: String -> [Float]
9. chisquare :: [Float] -> [Float] -> Float
10. rotate :: Int -> [a] -> [a]
11. crack :: String -> String
- programming technique
5.6 Chapter Remarks
the term comprehension comes from the axiom of comprehension (?)
in Set Theory which makes precise the idea of constructing a set by selecting a
ll values that satisfy a given property
5.7 Exercises
Chapter 6: Recursive Functions
recursion is the basic mechanism for looping in Haskell.
we start with integers, extend to recursion on lists, consider multiple
arguments, multiple recursion, and mutual recursion
6.1
6.2
6.3
6.4
6.5
6.6
6.7
6.8

Basic Concepts
Recursions on Lists
Multiple arguments
Multiple recursion
Mutual Recursion
Advice on Recursion
Chapter Remarks
Exercises

Chapter 7: Higher Order Functions


allow common programming patterns to be encapsulated as functions.
7.1 Basic Concepts
Revision: Functions with multiple arguments are usually curried.
Curried functions can be partially applied
Technically HoFs are functions that are either passed as parameters or returned
as results
This chapter focuses on the former
HoFs can be used to implement DSLs in Haskell.

7.2 Processing Lists


a simple "dsl" for processing lists
map :: (a -> b) -> [a] -> [b]
map f xs = [f x | x <- xs]

(1) map is a polymorphic function that can be applied to lists of any ty


pe
(2) it can be applied to itself to process nested lists.
note: Prelude> :t (map (+1))
(map (+1)) :: Num b => [b] -> [b]
iow this signature is c -> d where c = d = [b] (so fits as parame
ter for map)
and the next argument to the outer map should be [c] = [[b]], res
ult has type [d] = [[b]].
map (map (+1)) [[1,2,3],[4,5]]
= applying outer map
[(map (+1)) [1,2,3], (map (+1)) [4,5]]
= applying inner maps, twice
[[2,3,4],[5,6]]
(3) map can be defined recursively
map f [] = []
map f (x:xs) (f x) : map f xs
Another useful higher order function is filter, which selects all elemen
ts of a list that can satisfy a predicate, where a predicate is a function that
returns a boolean value
filter :: (a -> Bool) -> [a] -> [a]
filter p xs [x | x <- xs, p x]
filter even [1..10] -> [2.4,6,8,10]
As with map, filter can be applied to lists of any type, and can be defi
ned recursively (note: no stacked application)
filter :: (a -> Bool) -> [a] -> [a]
filter f [] = []
filter f (x : xs) | f x = x : filter f xs
| otherwise filter f xs
Map and filter are often used togethere in programs, with filter selecting eleme
nts from a list, and map transforming the selected elements.
Some other HoFs defined in the standard prelude
all even [2,4,6,8] -> True
any odd [2,4,6,8] -> False
takeWhile even [2,4,6,7,8] -> [2,4,6]
dropWhile odd [1,3,5,6,7] -> [6,7]
7.3 The foldr function
Consider
sum :: Num a => [a] -> a
sum [] = 0
sum (x:xs) = x + sum xs
product :: Num a => [a] -> a
product [] = 1
product (x:xs) = x * product xs

or :: [Bool] -> Bool


or [] = False
or (x:xs) => x || or xs
and :: [Bool] -> Bool
and [] = True
and (x:xs) => x && and xs
The repeating pattern is
f [] = v
f (x:xs) = x # f xs
The higher order function foldr encapsulates this pattern thus
-- non Foldable version
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f v [] = v
foldr f v (x:xs) = f x (foldr f v xs)
then
sum :: Num a => [a] -> a
sum = foldr (+) 0 -- note partially applied function returns a function of sig [
a] -> b
Prelude> :t (foldr (+) 0)
(foldr (+) 0) :: (Num b) => [b] -> b -- which is the same signature as sum has
product :: Num a => [a] -> a
product foldr (*) 1
or :: [Bool] -> Bool
or = foldr (||) False
and :: [Bool] -> Bool
and = foldr (&&) True
so the foldr function maps the empty list to the value v
and any non empty list to the function f applied to the head of the list, follow
ed by the recursively processed tail
In practice it is better to think of foldr in a non recursive manner, replacing
each cons operator in a list by the function f and the empty list at the end by
the value v
thus
foldr (+) 0 [1,2,3,4]
= list notation
foldr (+) 0 1:(2 : (3 : []))
= applying above idea
foldr 1 + (2 + (3 + 0))
= addition
6
(key) Though foldr encapsulates a simple pattern of recursion, it can be used to
define many more patterns than might be expected.
Consider
length :: [a] -> Int

length [] = 0
length (_ : xs) = 1 + length xs
this is equal to
length = foldr (\_ n -> 1 + n) 0
length [1,2,3]
= defn of length
foldr (\_ n -> 1 + n) 0 [1,2,3]
= list notation
foldr (\_ n -> 1 + n) 0 1:(2 : (3 : []))
= abusing notation a bit and idea above
1 `(\_ n -> 1+n)` ( 2 `(\_ n -> 1+n)` ( 3 `(\_ n -> 1+n)` 0))
= reducing rightmost term which is (\_ n -> 1+n) 3 0
1 `(\_ n -> 1+n)` ( 2 `(\_ n -> 1+n)` 1)
= reducing rightmost term which is (\_ n -> 1+n) 2 1
1 `(\_ n -> 1+n)` 2
= this is (\_ n -> 1+n) 1 2, addition
3
We conclude by noting that foldr reflects the use of an operator that is assumed
to associate to the right
foldr (+) [1,2,3,4] = 1 + (2 + (3 + (4 + 0)) = 1 + 2 + 3 + 4 + 0 with a right a
ssociating +
7.4 The foldl function
It is possible to define recursive functions on lists using an operator that ass
ociates to the left
e.g: the function sum can be redefined in this manner by using an auxilliary fun
ction sum' that takes an extra argument acc that accumulates the final result
sum :: Num a => [a] -> a
sum = sum' 0
where
sum' acc [] = acc
sum' acc (x:xs) = sum' (acc + x) xs -- note tail recursive
e.g
sum [1,2,3]
= definition of sum
sum' 0 [1,2,3]
= notation
sum' 0 (1 : [2,3])
= clause 2 of sum'
sum' (0 + 1) [2,3]
= clause 2 of sum'
sum ((0 + 1) + 2) [3]
=clause 2 of sum'
sum (((0 + 1) + 2) + 3) []
= clause 1 of sum1
(((0 + 1) + 2) + 3)
=addition
6
This is equivalent to 'replace with f and v in the list, but with f associating

the left'
sum = foldl (+) 0
sum [1,2,3] = foldl (+) 0 [1,2,3] = foldl (+) 0 1:(2:(3:[])) =
((0 + 1) + 2) + 3 = 0 + 1 + 2 + 3 where + associates to the left
(note the value v comes in the beginning
The pattern in recursion is thus
f v [] = v
f v (x:xs) = f (v # x) xs

For contrast the foldr recursion has the pattern


f [] = v
f (x:xs) = x # f xs
The foldl function is defined as
foldl :: ( a -> b -> a) -> a -> [b] -> a
foldl f v [] = v
foldl f v (x : xs) = foldl f (f v x) xs
For contrast, the foldr definition is
-- non Foldable version
foldr :: (b -> a -> a) -> a -> [b] -> a
foldr f v [] = v
foldr f v (x:xs) = f x (foldr f v xs)
Key to decoding: the value v has the type 'a'. The list has elements of the type
'b'
In practice however, it is better to think of foldl in a non recursive manner, i
n terms of an operator # that is assumed to be left associative
foldl (#) v [x0, x1, x2 ...xn]== ( ...((v # x0) # x1) ....) # xn
7.5 The composition operator
The higher order library operator . returns the composition of the two functions
as a single function and can be defined as belom
(.) :: (b -> c) -> (a -> b) -> (a -> c)
f . g = \x -> f (g x)
Composition can be used to simplify nested function applications by reducing par
antheses, and avoiding the need to explicitly refer to the initial argument.
e.g
odd n = not ( even n) == odd = not . even
twice f x = f ( f x) == twice = (f . f)
sumsqreven ns = sum (map (^2) (filter even ns)) == sumsqreven = sum . map (^2).
(filter even)
The last definition exploits the fact that (key) composition is associative
i.e f . (g .h) = (f . g) . h for any function f g h with appropriate types
So (key) for the composition of three or more functions there is no need to indi
cate the order of association.

Composition also has an identity given by the identity function


id :: a -> a
id = \x -> x
id is the function that simply returns the argument unchanged, and has the prope
rty
id . f = f . id = f for any function f
This is useful when reasoning about programs, and provides a starting point for
a sequence of compositions
(key) useful when defining composition of a list of functions thus
composelist :: [a -> a] -> (a -> a)
composelist = foldr (.) id
7.6
7.7
7.8
7.9

The Binary String Transmitter


voting algorithms
Chapter Remarks
Exercises

Chapter 8: Declaring Types and Classes


8.1 Type Declarations
8.2 Data declarations
8.3 Newtype declarations
8.4 Recursive Types
8.5 Class and Instance Declarations
8.6 Tautology Checker
8.7 Abstract Machine
8.8 Chapter Remarks
8.9 Exercises
Chapter 9: The Countdown problem
9.1 Introduction
9.2 Arithmetic Operators
9.3 Numeric Expressions
9.4 Combinatorial Functions
9.5 Formalizing the problem
9.6 Brute Force Solutions
9.7 Performance Testing
9.8 Combining generation and evaluation
9.9 Exploiting algebraic properties
9.10 Chapter Remarks
9.11 Exercises
Part 2
Chapter 10: Interactive Programming
10.1 The problem
10.2 The solution
10.3 Basic actions
10.4 Sequencing
10.5 Derived Properties
10.6 Hangman
10.7 Nim
10.8 Life
10.9 Chapter Remarks

10.10 Exercises
Chapter 11: Unbeatable TicTacToe
11.1 Introduction
11.2 Basic declarations
11.3 Grid utilities
11.4 Displaying a grid
11.5 Making a move
11.6 Reading a number
11.7 Human vs Human
11.8 Game Trees
11.9 Pruning the tree
11.10 Minimax algorithm
11.11 Human vs Computer
11.12 Chapter Remarks
11.13 Exercises
Chapter 12: Monads and More
12.1 Functors
12.2 Applicatives
12.3 Monads
12.4 Chapter Remarks
12.5 Exercises
Chapter 13: Monadic Parsing
13.1 What is a parser?
13.2 Parsers as functions
13.3 Basic definitions
13.4 Sequencing Parsers
13.5 Making choices
13.6 Derived Primitives
13.7 Handling spacing
13.8 Arithmetic Expressions
13.9 Calculator
13,10 Chapter Remarks
13.11 Exercises
13.8
Chapter 14: Foldables and Friends
14.1 Monoids
14.2 Foldables
14.3 Traversables
14.4 Chapter Remarks
14.5 Exercises
Chapter 15: Lazy Evaluation
15.1 Introduction
15.2 Evaluation Strategies
15.3 Termination
15.4 Number of reductions
15.5 Infinite Structures
15.6 Modular Programming
15.7 Strict Application
15.8 Chapter Remarks
15.9 Exercises
Chapter 16: Reasoning about programs
16.1 Equational Reasoning

16.2
16.3
16.4
16.5
16.6
16.7
16.8
16.9

Reasoning about Haskell


Simple Examples
Induction on Numbers
Induction on lists
Making append vanish
Compiler Correctness
Chapter Remarks
Exercises

Chapter 17: Calculating Compilers


17.1 Introduction
17.2 Syntax and Semantics
17.3 Adding a stack
17.4 Adding a continuation
17.5 Defunctionalizing
17.6 Combining the steps
17.7 Chapter Remarks
17.8 Exercises

You might also like