Professional Documents
Culture Documents
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
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
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.
*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
read
read
read
read
read
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
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]
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
Basic Concepts
Recursions on Lists
Multiple arguments
Multiple recursion
Mutual Recursion
Advice on Recursion
Chapter Remarks
Exercises
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
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