98
Introduction to Functional Programming Using Haskell Shin-Cheng Mu Acadmia Sinica FLOLAC 2014 1 / 85

[FLOLAC'14][scm] Functional Programming Using Haskell

Embed Size (px)

DESCRIPTION

Instructed by Shin-Cheng Mu on 2014 Formosan Summer School of Logic, Language and Computation

Citation preview

Page 1: [FLOLAC'14][scm] Functional Programming Using Haskell

Introduction to Functional ProgrammingUsing Haskell

Shin-Cheng Mu

Acadmia Sinica

FLOLAC 2014

1 / 85

Page 2: [FLOLAC'14][scm] Functional Programming Using Haskell

A Quick Introduction to Haskell

• We will be using the Glasgow Haskell Compiler (GHC).

• A Haskell compiler written in Haskell, with an interpreter thatboth interprets and runs compiled code.

• Installation: the Haskell Platform:http://hackage.haskell.org/platform/

2 / 85

Page 3: [FLOLAC'14][scm] Functional Programming Using Haskell

Function Definition

• A function definition consists of a type declaration, and thedefinition of its body:

square :: Int → Intsquare x = x × x ,

smaller :: Int → Int → Intsmaller x y = if x ≤ y then x else y .

• The GHCi interpreter evaluates expressions in the loadedcontext:

? square 376814197824? square (smaller 5 (3 + 4))25

3 / 85

Page 4: [FLOLAC'14][scm] Functional Programming Using Haskell

Evaluation

One possible sequence of evaluating (simplifying, or reducing)square (3 + 4):

square (3 + 4)

= { definition of + }square 7

= { definition of square }7× 7

= { definition of × }49 .

4 / 85

Page 5: [FLOLAC'14][scm] Functional Programming Using Haskell

Evaluation

One possible sequence of evaluating (simplifying, or reducing)square (3 + 4):

square (3 + 4)= { definition of + }

square 7

= { definition of square }7× 7

= { definition of × }49 .

4 / 85

Page 6: [FLOLAC'14][scm] Functional Programming Using Haskell

Evaluation

One possible sequence of evaluating (simplifying, or reducing)square (3 + 4):

square (3 + 4)= { definition of + }

square 7= { definition of square }

7× 7

= { definition of × }49 .

4 / 85

Page 7: [FLOLAC'14][scm] Functional Programming Using Haskell

Evaluation

One possible sequence of evaluating (simplifying, or reducing)square (3 + 4):

square (3 + 4)= { definition of + }

square 7= { definition of square }

7× 7= { definition of × }

49 .

4 / 85

Page 8: [FLOLAC'14][scm] Functional Programming Using Haskell

Another Evaluation Sequence

• Another possible reduction sequence:

square (3 + 4)

= { definition of square }(3 + 4)× (3 + 4)

= { definition of + }7× (3 + 4)

= { definition of + }7× 7

= { definition of × }49 .

• In this sequence the rule for square is applied first. The finalresult stays the same.

• Do different evaluations orders always yield the same thing?

5 / 85

Page 9: [FLOLAC'14][scm] Functional Programming Using Haskell

Another Evaluation Sequence

• Another possible reduction sequence:

square (3 + 4)= { definition of square }

(3 + 4)× (3 + 4)

= { definition of + }7× (3 + 4)

= { definition of + }7× 7

= { definition of × }49 .

• In this sequence the rule for square is applied first. The finalresult stays the same.

• Do different evaluations orders always yield the same thing?

5 / 85

Page 10: [FLOLAC'14][scm] Functional Programming Using Haskell

Another Evaluation Sequence

• Another possible reduction sequence:

square (3 + 4)= { definition of square }

(3 + 4)× (3 + 4)= { definition of + }

7× (3 + 4)

= { definition of + }7× 7

= { definition of × }49 .

• In this sequence the rule for square is applied first. The finalresult stays the same.

• Do different evaluations orders always yield the same thing?

5 / 85

Page 11: [FLOLAC'14][scm] Functional Programming Using Haskell

Another Evaluation Sequence

• Another possible reduction sequence:

square (3 + 4)= { definition of square }

(3 + 4)× (3 + 4)= { definition of + }

7× (3 + 4)= { definition of + }

7× 7

= { definition of × }49 .

• In this sequence the rule for square is applied first. The finalresult stays the same.

• Do different evaluations orders always yield the same thing?

5 / 85

Page 12: [FLOLAC'14][scm] Functional Programming Using Haskell

Another Evaluation Sequence

• Another possible reduction sequence:

square (3 + 4)= { definition of square }

(3 + 4)× (3 + 4)= { definition of + }

7× (3 + 4)= { definition of + }

7× 7= { definition of × }

49 .

• In this sequence the rule for square is applied first. The finalresult stays the same.

• Do different evaluations orders always yield the same thing?

5 / 85

Page 13: [FLOLAC'14][scm] Functional Programming Using Haskell

A Non-terminating Reduction

• Consider the following program:

infinity :: Intinfinity = infinity + 1 .

• Try evaluating square infinity .

square infinity= { definition of square }

infinity × infinity= { definition of infinity }

(infinity + 1)× infinity= ((infinity + 1) + 1)× infinity . . .

• Some expressions have a terminating reduction sequence,some do not. This is related to one of the central issues inthis course.

6 / 85

Page 14: [FLOLAC'14][scm] Functional Programming Using Haskell

And, a Terminating Reduction

• In the previous case, the reduction does not terminatebecause, by rule, to evaluate x + y we have to know what x is.

• Consider the following program:

three :: Int → Intthree x = 3 .

• Try evaluating three infinity .

7 / 85

Page 15: [FLOLAC'14][scm] Functional Programming Using Haskell

And, a Terminating Reduction

• If we start with simplifying three:

three infinity= { definition of three }

3 .

• It terminates because three x need not know what x is.

• Haskell uses a peculiar reduction strategy called lazyevaluation that evaluates an expression only when it reallyneeds to. It allows more program to terminate• (while causing lots of other troubles for some people).

• We will not go into this feature in this course. For now, it issufficient to know• that some expression terminate, some do not, and• it is in general good to know that an expression may terminate.

8 / 85

Page 16: [FLOLAC'14][scm] Functional Programming Using Haskell

Mathematical Functions

• Mathematically, a function is a mapping between argumentsand results.• A function f :: A→ B maps each element in A to a unique

element in B.

• In contrast, C “functions” are not mathematical functions:• int y = 1; int f (x:int) { return ((y++) * x); }

• Functions in Haskell have no such side-effects:(unconstrained) assignments, IO, etc.

• Why removing these useful features? We will talk about thatlater in this course.

9 / 85

Page 17: [FLOLAC'14][scm] Functional Programming Using Haskell

Curried Functions

• Consider again the function smaller :

smaller :: Int → Int → Intsmaller x y = if x ≤ y then x else y

• We sometimes informally call it a function “taking twoarguments”.

• Usage: smaller 3 4.

• Strictly speaking, however, smaller is a function returning afunction. The type should be bracketed as Int → (Int → Int).

10 / 85

Page 18: [FLOLAC'14][scm] Functional Programming Using Haskell

Precedence and Association

• In a sense, all Haskell functions takes exactly one argument.• Such functions are often called curried.

• Type: a→ b → c = a→ (b → c), not (a→ b)→ c .

• Application: f x y = (f x) y , not f (x y).• smaller 3 4 means (smaller 3) 4.• square square 3 means (square square) 3, which results in a

type error.

• Function application binds tighter than infix operators. E.g.square 3 + 4 means (square 3) + 4.

11 / 85

Page 19: [FLOLAC'14][scm] Functional Programming Using Haskell

Why Currying?

• It exposes more chances to reuse a function, since it can bepartially applied.

twice :: (a→ a)→ (a→ a)twice f x = f (f x) ,

quad :: Int → Intquad = twice square .

• Try evaluating quad 3:

quad 3

= twice square 3

= square (square 3)

= . . .

12 / 85

Page 20: [FLOLAC'14][scm] Functional Programming Using Haskell

Sectioning

• Infix operators are curried too. The operator (+) may havetype Int → Int → Int.

• Infix operator can be partially applied too.

(x ⊕) y = x ⊕ y ,(⊕ y) x = x ⊕ y .

• (1 +) :: Int → Int increments its argument by one.• (1.0 /) :: Float → Float is the “reciprocal” function.• (/ 2.0) :: Float → Float is the “halving” function.

13 / 85

Page 21: [FLOLAC'14][scm] Functional Programming Using Haskell

Infix and Prefix

• To use an infix operator in prefix position, surrounded it inparentheses. For example, (+) 3 4 is equivalent to 3 + 4.

• Surround an ordinary function by back-quotes (not quotes!)to put it in infix position. E.g. 3 ‘mod ‘ 4 is the same asmod 3 4.

14 / 85

Page 22: [FLOLAC'14][scm] Functional Programming Using Haskell

Function Composition

• Functions composition:

(·) :: (b → c)→ (a→ b)→ (a→ c)(f · g) x = f (g x) .

• E.g. another way to write quad :

quad :: Int → Intquad = square · square .

• Some important properties:• id · f = f = f · id , where id x = x .• (f · g) · h = f · (g · h).

15 / 85

Page 23: [FLOLAC'14][scm] Functional Programming Using Haskell

Guarded Equations

• Recall the definition:

smaller :: Int → Int → Intsmaller x y = if x ≤ y then x else y .

• We can also write:

smaller :: Int → Int → Intsmaller x y | x ≤ y = x

| x > y = y .

• Helpful when there are many choices:

signum :: Int → Intsignum x | x > 0 = 1

| x == 0 = 0| x < 0 = −1 .

16 / 85

Page 24: [FLOLAC'14][scm] Functional Programming Using Haskell

λ Expressions

• Since functions are first-class constructs, we can alsoconstruct functions in expressions.

• A λ expression denotes an anonymous function.• λx → e: a function with argument x and body e.• λx → λy → e abbreviates to λx y → e.• In ASCII, we write λ as \

• Examples:• (λx → x × x) 3 = 9.• (λx y → x × x + 2× y) 3 4 = 17.

17 / 85

Page 25: [FLOLAC'14][scm] Functional Programming Using Haskell

λ Expressions

• Yet another way to define smaller :

smaller :: Int → Int → Intsmaller = λx y → if x ≤ y then x else y .

• Why λs? Sometimes we may want to quickly define afunction and use it only once.

• In fact, λ is a more primitive concept.

18 / 85

Page 26: [FLOLAC'14][scm] Functional Programming Using Haskell

Local Definitions

There are two ways to define local bindings in Haskell.

• let-expression:

f :: (Float,Float)→ Floatf (x , y) = let a = (x + y)/2

b = (x + y)/3in (a + 1)× (b + 2) .

• where-clause:

f :: Int → Int → Intf x y | x ≤ 10 = x + a

| x > 10 = x − awhere a = square (y + 1) .

• let can be used in expressions (e.g. 1 + (let..in..)), whilewhere qualifies multiple guarded equations.

19 / 85

Page 27: [FLOLAC'14][scm] Functional Programming Using Haskell

Types

• The universe of values is partitioned into collections, calledtypes.

• Some basic types: Int, Float, Bool , Char . . .

• Type “constructors”: functions, lists, trees . . . to beintroduced later.

• Operations on values of a certain type might not make sensefor other types. For example: square square 3.

20 / 85

Page 28: [FLOLAC'14][scm] Functional Programming Using Haskell

Strong Typing

• Strong typing: the type of a well-formed expression can bededucted from the constituents of the expression.

• It helps you to detect errors.

• More importantly, programmers may consider the types forthe values being defined before considering the definitionthemselves, leading to clear and well-structured programs.

21 / 85

Page 29: [FLOLAC'14][scm] Functional Programming Using Haskell

Booleans

The datatype Bool can be introduced with a datatype declaration:

data Bool = False | True .

(But you need not do so. The type Bool is already defined in theHaskell Prelude.)

22 / 85

Page 30: [FLOLAC'14][scm] Functional Programming Using Haskell

Datatype Declaration

• In Haskell, a data declaration defines a new type.

data Type = Con1 Type11 Type12 . . .| Con2 Type21 Type22 . . .| :

• The declaration above introduces a new type, Type, withseveral cases.

• Each case starts with a constructor, and several (zero ormore) arguments (also types).

• Informally it means “a value of type Type is either a Con1

with arguments Type11, Type12. . . , or a Con2 with argumentsType21, Type22. . . ”

• Types and constructors begin in capital letters.

23 / 85

Page 31: [FLOLAC'14][scm] Functional Programming Using Haskell

Functions on Booleans

Negation:

not :: Bool → Boolnot False = Truenot True = False .

• Notice the definition by pattern matching. The definition hastwo cases, because Bool is defined by two cases. The shape ofthe function follows the shape of its argument.

24 / 85

Page 32: [FLOLAC'14][scm] Functional Programming Using Haskell

Functions on Booleans

Conjunction and disjunction:

(&&), (||) :: Bool → Bool → BoolFalse && x = FalseTrue && x = x ,

False || x = xTrue || x = True .

25 / 85

Page 33: [FLOLAC'14][scm] Functional Programming Using Haskell

Functions on Booleans

Equality check:

(==), (6=) :: Bool → Bool → Boolx == y = (x && y) || (not x && not y)

x 6= y = not (x == y) .

• = is a definition, while == is a function.

• 6= is written / = in ASCII.

26 / 85

Page 34: [FLOLAC'14][scm] Functional Programming Using Haskell

Example

leapyear :: Int → Boolleapyear y = (y ‘mod ‘ 4 == 0) &&

(y ‘mod ‘ 100 6= 0 || y ‘mod ‘ 400 == 0) .

• Note: y ‘mod ‘ 100 could be written mod y 100. Thebackquotes turns an ordinary function to an infix operator.

• It’s just personal preference whether to do so.

27 / 85

Page 35: [FLOLAC'14][scm] Functional Programming Using Haskell

Characters

• You can think of Char as a big data definition:

data Char = 'a' | 'b' | . . .

with functions:

ord :: Char → Intchr :: Int → Char .

• Characters are compared by their order:

isDigit :: Char → BoolisDigit x = '0' ≤ x && x ≤ '9' .

28 / 85

Page 36: [FLOLAC'14][scm] Functional Programming Using Haskell

Tuples

• The polymorphic type (a, b) is essentially the same as thefollowing declaration:

data Pair a b = MkPair a b .

• Or, had Haskell allow us to use symbols:

data (a, b) = (a, b) .

• Two projections:

fst :: (a, b)→ afst (a, b) = a ,

snd :: (a, b)→ bsnd (a, b) = b .

29 / 85

Page 37: [FLOLAC'14][scm] Functional Programming Using Haskell

Either and Maybe

• Another standard datatype Either a b states that a value is“either an a, or a b.”

data Either a b = Left a | Right b .

• Another standard datatype Either a b states that a value is“either an a, or a b.”

data Maybe a = Just a | Nothing .

• We will see their usage in practicals.

30 / 85

Page 38: [FLOLAC'14][scm] Functional Programming Using Haskell

Pattern Matching

• Previously, pattern matching only happens in the LHS of afunction definition:

f (x , y) = . . .

g (Left x) = . . .g (Right x) = . . .

• You can also patten-match in a let-binding:

f x = let (y , z) = . . . in . . .

31 / 85

Page 39: [FLOLAC'14][scm] Functional Programming Using Haskell

case-expressions

• When there are more cases, you can use a case-expression:

f x = case some expression ofLeft y → . . .Right z → . . .

• Both let and case can appear deep in expressions, be nested,etc.

• In fact, patterns are eventually translated to case expressionsin the GHC core language.

32 / 85

Page 40: [FLOLAC'14][scm] Functional Programming Using Haskell

What Are the Smaller Letters in Types?

• You might have noticed something odd in types of functions.For example fst may be applied to pairs of many differenttypes:• fst :: (Int,Char)→ Int,• fst :: (Char , Int)→ Char ,• fst :: (Bool ,Char)→ Bool . . .

• We give it a type that covers all the cases above: for all a andb, fst :: (a, b)→ a.

• The type can be instantiated to any of the types above, whenfst is used.

33 / 85

Page 41: [FLOLAC'14][scm] Functional Programming Using Haskell

Parametric Polymorphic Types

• Polymorphism: allowing one term to have many types.

• In the style we have seen above, the “many types” of fst areselected by assigning different types (Int, Bool , etc) toparameters (a and b).

• This style is thus called parametric polymorphism.

• Note that, in this style, the same function (the samealgorithm) can be applied to many different types, because itdoes not matter to the algorithm what they are.• E.g., fst does not need to know what a and b are in its input

(a, b).

34 / 85

Page 42: [FLOLAC'14][scm] Functional Programming Using Haskell

Equality Check

• You might have also noticed that we can check for equality ofa number of types:• (==) :: Char → Char → Bool ,• (==) :: Int → Int → Bool ,• (==) :: (Int,Char)→ (Int,Char)→ Bool ,• (==) :: [Int]→ [Int]→ Bool . . .

• It differs from the situation of fst: the algorithm for checkingequality for each type is different. We just use the same nameto refer to them, for convenience.

35 / 85

Page 43: [FLOLAC'14][scm] Functional Programming Using Haskell

Equality Check

• Furthermore, not all types have (==) defined. For example,we cannot check whether two functions are equal.

• (==) is an overloaded name — one name shared by manydifferent definitions of equalities, for different types.

• It is some times called ad-hoc polymorphism.

36 / 85

Page 44: [FLOLAC'14][scm] Functional Programming Using Haskell

Ad-Hoc Polymorphism

• Haskell deals with overloading by a general mechanism calledtype classes. It is considered a major feature of Haskell.

• While the type class is an interesting topic, we might notcover much of it since it is orthogonal to the central messageof this course.

• We only need to know that if a function uses (==), it isnoted in the type.• E.g. The function pos x xs finds the position of the first

occurrence of x in xs. It needs to use equality check.• It my have type pos :: Eq a⇒ a→ [a]→ Int.• Eq a says that “a cannot be any type. It should be a type for

which (==) is defined.”

37 / 85

Page 45: [FLOLAC'14][scm] Functional Programming Using Haskell

Lists in Haskell

• Lists: conceptually, sequences of things.

• Traditionally an important datatype in functional languages.

• In Haskell, all elements in a list must be of the same type.• [1, 2, 3, 4] :: [Int]• [True,False,True] :: [Bool ]• [[1, 2], [ ], [6, 7]] :: [[Int]]• [ ] :: [a], the empty list (whose element type is not determined).

38 / 85

Page 46: [FLOLAC'14][scm] Functional Programming Using Haskell

List as a Datatype

• [ ] :: [a] is the empty list whose element type is not determined.

• If a list is non-empty, the leftmost element is called its headand the rest its tail.

• The constructor (:) :: a→ [a]→ [a] builds a list. E.g. inx : xs, x is the head and xs the tail of the new list.

• You can think of a list as being defined by

data [a] = [ ] | a : [a] .

• [1, 2, 3] is an abbreviation of 1 : (2 : (3 : [ ])).

39 / 85

Page 47: [FLOLAC'14][scm] Functional Programming Using Haskell

Characters and Strings

• In Haskell, strings are treated as lists of characters.

• Thus when we write "abc", it is treated as an abbreviation of['a', 'b', 'c'].

• It makes string processing convenient, while having someefficiency issues.

• For efficiency, there are libraries providing “block”representation of strings.

40 / 85

Page 48: [FLOLAC'14][scm] Functional Programming Using Haskell

Head and Tail

• head :: [a]→ a. e.g. head [1, 2, 3] = 1.

• tail :: [a]→ [a]. e.g. tail [1, 2, 3] = [2, 3].

• init :: [a]→ [a]. e.g. init [1, 2, 3] = [1, 2].

• last :: [a]→ a. e.g. last [1, 2, 3] = 3.

• They are all partial functions on non-empty lists. e.g. head [ ]crashes with an error message.

• null :: [a]→ Bool checks whether a list is empty.

41 / 85

Page 49: [FLOLAC'14][scm] Functional Programming Using Haskell

List Generation

• [0..25] generates the list [0, 1, 2..25].

• [0, 2..25] yields [0, 2, 4..24].

• [2..0] yields [].

• The same works for all ordered types. For example Char :• ['a'..'z'] yields ['a', 'b', 'c'..'z'].

• [1..] yields the infinite list [1, 2, 3..].

42 / 85

Page 50: [FLOLAC'14][scm] Functional Programming Using Haskell

List Comprehension

• Some functional languages provide a convenient notation forlist generation. It can be defined in terms of simpler functions.

• e.g. [x × x | x ← [1..5], odd x ] = [1, 9, 25].

• Syntax: [e | Q1,Q2..]. Each Qi is either• a generator x ← xs, where x is a (local) variable or pattern of

type a while xs is an expression yielding a list of type [a], or• a guard, a boolean valued expression (e.g. odd x).• e is an expression that can involve new local variables

introduced by the generators.

43 / 85

Page 51: [FLOLAC'14][scm] Functional Programming Using Haskell

List Comprehension

Examples:

• [(a, b) | a← [1..3], b ← [1..2]] =[(1, 1), (1, 2), (2, 1), (2, 2), (3, 1), (3, 2)]

• [(a, b) | b ← [1..2], a← [1..3]] =[(1, 1), (2, 1), (3, 1), (1, 2), (2, 2), (3, 2)]

• [(i , j) | i ← [1..4], j ← [i + 1..4]] =[(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]

• [(i , j) |← [1..4], even i , j ← [i + 1..4], odd j ] = [(2, 3)]

44 / 85

Page 52: [FLOLAC'14][scm] Functional Programming Using Haskell

Inductively Defined Lists

• Recall that a (finite) list can be seen as a datatype definedby: 1

data [a] = [] | a : [a] .

• Every list is built from the base case [ ], with elements addedby (:) one by one: [1, 2, 3] = 1 : (2 : (3 : [ ])).

• The type [a] is the smallest set such that

1 [ ] is in [a];2 if xs is in [a] and x is in a, x : xs is in [a] as well.

• In fact, Haskell allows you to build infinitely long lists (onethat never reaches [ ]). However. . .• for now let’s consider finite lists only, as having infinite lists

make the semantics much more complicated. 2

1Not a real Haskell definition.2What does that mean? We will talk about it later.

45 / 85

Page 53: [FLOLAC'14][scm] Functional Programming Using Haskell

Inductively Defined Functions on Lists

• Many functions on lists can be defined according to how a listis defined:

sum :: [Int]→ Intsum [ ] = 0sum (x : xs) = x + sum xs .

allEven :: Int → BoolallEven [ ] = TrueallEven f (x : xs) = even x && allEven xs .

46 / 85

Page 54: [FLOLAC'14][scm] Functional Programming Using Haskell

Length

• The function length defined inductively:

length :: [a]→ Intlength [ ] = 0length (x : xs) = 1+ length xs .

47 / 85

Page 55: [FLOLAC'14][scm] Functional Programming Using Haskell

List Append

• The function (++) appends two lists into one

(++) :: [a]→ [a]→ [a][ ] ++ ys = ys(x : xs) ++ ys = x : (xs ++ ys) .

• Examples:• [1, 2] ++[3, 4, 5] = [1, 2, 3, 4, 5],• [ ] ++[3, 4, 5] = [3, 4, 5] = [3, 4, 5] ++[ ].

• Compare with (:) :: a→ [a]→ [a]. It is a type error to write[ ] : [3, 4, 5]. (++) is defined in terms of (:).

• Is the following true?

length (xs ++ ys) = length xs + length ys .

• In words, length distributes into (++).• How can we convince ourselves that the property holds. That

is, how can we prove it?

48 / 85

Page 56: [FLOLAC'14][scm] Functional Programming Using Haskell

Concatenation

• While (++) repeatedly applies (:), the function concatrepeatedly calls (++):

concat :: [[a]]→ [a]concat [ ] = [ ]concat (xs : xss) = xs ++ concat xss .

• E.g. concat [[1, 2], [ ], [3, 4], [5]] = [1, 2, 3, 4, 5].

• Compare with sum.

• Is it always true that sum · concat = sum ·map sum?

49 / 85

Page 57: [FLOLAC'14][scm] Functional Programming Using Haskell

Definition by Induction/Recursion

• Rather than giving commands, in functional programming wespecify values; instead of performing repeated actions, wedefine values on inductively defined structures.

• Thus induction (or in general, recursion) is the only “controlstructure” we have. (We do identify and abstract over plentyof patterns of recursion, though.)

• To inductively define a function f on lists, we specify a valuefor the base case (f [ ]) and, assuming that f xs has beencomputed, consider how to construct f (x : xs) out of f xs.

50 / 85

Page 58: [FLOLAC'14][scm] Functional Programming Using Haskell

Filter

• filter :: (a→ Bool)→ [a]→ [a].• e.g. filter even [2, 7, 4, 3] = [2, 4]• filter (λn→ n ‘mod ‘ 3 == 0) [3, 2, 6, 7] = [3, 6]

• Application: count the number of occurrences of ′a′ in a list:• length · filter ('a' ==)• Or length · filter (λx → 'a' == x)

51 / 85

Page 59: [FLOLAC'14][scm] Functional Programming Using Haskell

Filter

• filter p xs keeps only those elements in xs that satisfy p.

filter :: (a→ Bool)→ [a]→ [a]filter p [ ] = [ ]filter p (x : xs) | p x = x : filter p xs

| otherwise = filter p xs .

52 / 85

Page 60: [FLOLAC'14][scm] Functional Programming Using Haskell

List Reversal

• reverse [1, 2, 3, 4] = [4, 3, 2, 1].

reverse :: [a]→ [a]reverse [ ] = [ ]reverse (x : xs) = reverse xs ++[x ] .

53 / 85

Page 61: [FLOLAC'14][scm] Functional Programming Using Haskell

Map

• map :: (a→ b)→ [a]→ [b] applies the given function to eachelement of the input list, forming a new list.

• For example, map (1+) [1, 2, 3, 4, 5] = [2, 3, 4, 5, 6].

• map square [1, 2, 3, 4] = [1, 4, 9, 16].

• How do you define map inductively?

54 / 85

Page 62: [FLOLAC'14][scm] Functional Programming Using Haskell

Map

• Observe that given f :: (a→ b), map f is a function, of type[a]→ [b], that can be defined inductively on lists.

map :: (a→ b)→ ([a]→ [b])map f [ ] = [ ]map f (x : xs) = f x : map f xs .

55 / 85

Page 63: [FLOLAC'14][scm] Functional Programming Using Haskell

Some More About Map

• Every once in a while you may need a small function whichyou do not want to give a name to. At such moments you canuse the λ notation:• map (λx → x × x) [1, 2, 3, 4] = [1, 4, 9, 16]

• Note: a list comprehension can always be translated into acombination of primitive list generators and map, filter , andconcat.

56 / 85

Page 64: [FLOLAC'14][scm] Functional Programming Using Haskell

TakeWhile

• takeWhile p xs yields the longest prefix of xs such that pholds for each element.

• E.g. takeWhile even [2, 4, 5, 6, 8] = [2, 4].

• Inductive definition:

takeWhile :: (a→ Bool)→ [a]→ [a]takeWhile p [ ] = [ ]takeWhile p (x : xs)| p x = x : takeWhile p xs| otherwise = [ ] .

57 / 85

Page 65: [FLOLAC'14][scm] Functional Programming Using Haskell

TakeWhile

• takeWhile p xs yields the longest prefix of xs such that pholds for each element.

• E.g. takeWhile even [2, 4, 5, 6, 8] = [2, 4].

• Inductive definition:

takeWhile :: (a→ Bool)→ [a]→ [a]takeWhile p [ ] = [ ]takeWhile p (x : xs)| p x = x : takeWhile p xs| otherwise = [ ] .

57 / 85

Page 66: [FLOLAC'14][scm] Functional Programming Using Haskell

DropWhile

• dropWhile p xs drops the prefix from xs.

• E.g. dropWhile even [2, 4, 5, 6, 8] = [5, 6, 8].

• Inductive definition:

dropWhile :: (a→ Bool)→ [a]→ [a]dropWhile p [ ] = [ ]dropWhile p (x : xs)| p x = dropWhile p xs| otherwise = x : xs .

• Property: takeWhile p xs ++ dropWhile p xs = xs.

58 / 85

Page 67: [FLOLAC'14][scm] Functional Programming Using Haskell

DropWhile

• dropWhile p xs drops the prefix from xs.

• E.g. dropWhile even [2, 4, 5, 6, 8] = [5, 6, 8].

• Inductive definition:

dropWhile :: (a→ Bool)→ [a]→ [a]dropWhile p [ ] = [ ]dropWhile p (x : xs)| p x = dropWhile p xs| otherwise = x : xs .

• Property: takeWhile p xs ++ dropWhile p xs = xs.

58 / 85

Page 68: [FLOLAC'14][scm] Functional Programming Using Haskell

All Prefixes and Suffixes

• inits [1, 2, 3] = [[ ], [1], [1, 2], [1, 2, 3]]

inits :: [a]→ [[a]]inits [ ] = [[ ]]inits (x : xs) = [ ] : map (x :) (inits xs) .

• tails [1, 2, 3] = [[1, 2, 3], [2, 3], [3], [ ]]

tails :: [a]→ [[a]]tails [ ] = [[ ]]tails (x : xs) = (x : xs) : tails xs .

59 / 85

Page 69: [FLOLAC'14][scm] Functional Programming Using Haskell

Totality

• Structure of our definitions so far:

f [ ] = . . .f (x : xs) = . . . f xs . . .

• Both the empty and the non-empty cases are covered,guaranteeing there is a matching clause for all inputs.

• The recursive call is made on a “smaller” argument,guranteeing termination.

• Together they guarantee that every input is mapped to someoutput. Thus they define total functions on lists.

60 / 85

Page 70: [FLOLAC'14][scm] Functional Programming Using Haskell

The So-Called “Mathematical Induction”

• Let P be a predicate on natural numbers.

• We’ve all learnt this principle of proof by induction: to provethat P holds for all natural numbers, it is sufficient to showthat• P 0 holds;• P (1 + n) holds provided that P n does.

61 / 85

Page 71: [FLOLAC'14][scm] Functional Programming Using Haskell

Proof by Induction on Natural Numbers

• We can see the above inductive principle as a result of seeingnatural numbers as defined by the datatype 3

data N = 0 | 1+ N .

• That is, any natural number is either 0, or 1+ n where n is anatural number.

• The type N is the smallest set such that

1 0 is in N;2 if n is in N, so is 1+ n.

• Thus to show that P holds for all natural numbers, we onlyneed to consider these two cases.

• In this lecture, 1+ is written in bold font to emphasise that itis a data constructor (as opposed to the function (+), to bedefined later, applied to a number 1).

3Not a real Haskell definition.62 / 85

Page 72: [FLOLAC'14][scm] Functional Programming Using Haskell

Inductively Defined Functions

• Since the type N is defined by two cases, it is natural to definefunctions on N following the structure:

exp :: N→ N→ Nexp b 0 = 1exp b (1+ n) = b × exp b n .

• Even addition can be defined inductively

(+) :: N→ N→ N0 + n = n(1+ m) + n = 1+ (m + n) .

• Exercise: define (×)?

63 / 85

Page 73: [FLOLAC'14][scm] Functional Programming Using Haskell

Without the n + k Pattern

• Unfortunately, newer versions of Haskell abandoned the“n + k pattern” used in the previous slide. And there is not abuilt-in type for N. Instead we have to write:

exp :: Int → Int → Intexp b 0 = 1exp b n = b × exp b (n − 1) .

• For the purpose of this course, the pattern 1 + n reveals thecorrespondence between N and lists, and matches our proofstyle. Thus we will use it in the lecture.

• Remember to remove them in your code.

64 / 85

Page 74: [FLOLAC'14][scm] Functional Programming Using Haskell

Lists and Natural Numbers

• We have yet to prove that (×) is associative.

• The proof is quite similar to the proof for associativity of(++), which we will talk about later.

• In fact, N and lists are closely related in structure.

• Most of us are used to think of numbers as atomic and lists asstructured data. Neither is necessarily true.

• For the rest of the course we will demonstrate induction usinglists, while taking the properties for N as given.

65 / 85

Page 75: [FLOLAC'14][scm] Functional Programming Using Haskell

Take and Drop

• take n takes the first n elements of the list.• For example, take 0 xs = [],• take 3 "abcde" = "abc",• take 3 "ab" = "ab".

• Dually, drop n drops the first n elements of the list.• For example, drop 0 xs = xs,• drop 3 "abcde" = "cd",• drop 3 "ab" = "".

• take n xs ++ drop n xs = xs, as long as evaluation of nterminates.

66 / 85

Page 76: [FLOLAC'14][scm] Functional Programming Using Haskell

Take and Drop

• They can be defined by induction on the first argument:

take :: N→ [a]→ [a]take 0 xs = [ ]take (1+ n) [ ] = [ ]take (1+ n) (x : xs) = x : take n xs .

drop :: N→ [a]→ [a]drop 0 xs = xsdrop (1+ n) [ ] = [ ]drop (1+ n) (x : xs) = drop n xs .

• Note: take n xs ++ drop n xs = xs, for all n and xs.

67 / 85

Page 77: [FLOLAC'14][scm] Functional Programming Using Haskell

Variations with the Base Case

• Some functions discriminate between several base cases. E.g.

fib :: N→ Nfib 0 = 0fib 1 = 1fib (2 + n) = fib (1 + n) + fib n .

68 / 85

Page 78: [FLOLAC'14][scm] Functional Programming Using Haskell

• Some functions make more sense when it is defined only onnon-empty lists:

f [x ] = . . .f (x : xs) = . . .

• What about totality?• They are in fact functions defined on a different datatype:

data [a]+ = Singleton a | a : [a]+ .

• We do not want to define map, filter again for [a]+. Thus wereuse [a] and pretend that we were talking about [a]+.

• It’s the same with N. We embedded N into Int.• Ideally we’d like to have some form of subtyping. But that

makes the type system more complex.

69 / 85

Page 79: [FLOLAC'14][scm] Functional Programming Using Haskell

Lexicographic Induction

• It also occurs often that we perform lexicographic inductionon multiple arguments: some arguments decrease in size,while others stay the same.

• E.g. the function merge merges two sorted lists into onesorted list:

merge :: [Int]→ [Int]→ [Int]merge [ ] [ ] = [ ]merge [ ] (y : ys) = y : ysmerge (x : xs) [ ] = x : xsmerge (x : xs) (y : ys)

| x ≤ y = x : merge xs (y : ys)| otherwise = y : merge (x : xs) ys .

70 / 85

Page 80: [FLOLAC'14][scm] Functional Programming Using Haskell

Zip

• zip :: [a]→ [b]→ [(a, b)]

• e.g. zip "abcde" [1, 2, 3] = [('a', 1), ('b', 2), ('c', 3)]

• The length of the resulting list is the length of the shorterinput list.

71 / 85

Page 81: [FLOLAC'14][scm] Functional Programming Using Haskell

Zip

zip :: [a]→ [b]→ [(a, b)]zip [ ] [ ] = [ ]zip [ ] (y : ys) = [ ]zip (x : xs) [ ] = [ ]zip (x : xs) (y : ys) = (x , y) : zip xs ys .

72 / 85

Page 82: [FLOLAC'14][scm] Functional Programming Using Haskell

Non-Structural Induction

• In most of the programs we’ve seen so far, the recursive callare made on direct sub-components of the input (e.g.f (x : xs) = ..f xs..). This is called structural induction.• It is relatively easy for compilers to recognise structural

induction and determine that a program terminates.

• In fact, we can be sure that a program terminates if thearguments get “smaller” under some (well-founded) ordering.

73 / 85

Page 83: [FLOLAC'14][scm] Functional Programming Using Haskell

Mergesort

• In the implemenation of mergesort below, for example, thearguments always get smaller in size.

msort :: [Int]→ [Int]msort [ ] = [ ]msort [x ] = [x ]msort xs = merge (msort ys) (msort zs) ,where n = length xs ‘div ‘ 2

ys = take n xszs = drop n xs .

• What if we omit the case for [x ]?

• If all cases are covered, and all recursive calls are applied tosmaller arguments, the program defines a total function.

74 / 85

Page 84: [FLOLAC'14][scm] Functional Programming Using Haskell

A Non-Terminating Definition

• Example of a function, where the argument to the recursivedoes not reduce in size:

f :: Int → Intf 0 = 0f n = f n .

• Certainly f is not a total function. Do such definitions“mean” something? We will talk about these later.

75 / 85

Page 85: [FLOLAC'14][scm] Functional Programming Using Haskell

A Common Pattern We’ve Seen ManyTimes. . .

• Many programs we have seen follow a similar pattern:

sum [ ] = 0sum (x : xs) = x + sum xs .

length [ ] = 0length (x : xs) = 1 + length xs .

map f [ ] = [ ]map f (x : xs) = f x : map f xs .

• This pattern is extracted and called foldr :

foldr f e [ ] = efoldr f e (x : xs) = f x (foldr f e xs) .

76 / 85

Page 86: [FLOLAC'14][scm] Functional Programming Using Haskell

Replacing Constructors

• The function foldr is among the most important functions onlists.

foldr :: (a→ b → b)→ b → [a]→ b

• One way to look at foldr (⊕) e is that it replaces [ ] with eand (:) with (⊕):

foldr (⊕) e [1, 2, 3, 4]= foldr (⊕) e (1 : (2 : (3 : (4 : [ ]))))= 1⊕ (2⊕ (3⊕ (4⊕ e))).

• sum = foldr (+) 0.

• One can see that id = foldr (:) [ ].

77 / 85

Page 87: [FLOLAC'14][scm] Functional Programming Using Haskell

Some Trivial Folds on Lists

• Function maximum returns the maximum element in a list:

• maximum = foldr max -∞.

• Function prod returns the product of a list:

• prod = foldr (×) 1.

• Function and returns the conjunction of a list:

• and = foldr (&&) True.

• Lets emphasise again that id on lists is a fold:

• id = foldr (:) [ ].

78 / 85

Page 88: [FLOLAC'14][scm] Functional Programming Using Haskell

Some Trivial Folds on Lists

• Function maximum returns the maximum element in a list:• maximum = foldr max -∞.

• Function prod returns the product of a list:

• prod = foldr (×) 1.

• Function and returns the conjunction of a list:

• and = foldr (&&) True.

• Lets emphasise again that id on lists is a fold:

• id = foldr (:) [ ].

78 / 85

Page 89: [FLOLAC'14][scm] Functional Programming Using Haskell

Some Trivial Folds on Lists

• Function maximum returns the maximum element in a list:• maximum = foldr max -∞.

• Function prod returns the product of a list:• prod = foldr (×) 1.

• Function and returns the conjunction of a list:

• and = foldr (&&) True.

• Lets emphasise again that id on lists is a fold:

• id = foldr (:) [ ].

78 / 85

Page 90: [FLOLAC'14][scm] Functional Programming Using Haskell

Some Trivial Folds on Lists

• Function maximum returns the maximum element in a list:• maximum = foldr max -∞.

• Function prod returns the product of a list:• prod = foldr (×) 1.

• Function and returns the conjunction of a list:• and = foldr (&&) True.

• Lets emphasise again that id on lists is a fold:

• id = foldr (:) [ ].

78 / 85

Page 91: [FLOLAC'14][scm] Functional Programming Using Haskell

Some Trivial Folds on Lists

• Function maximum returns the maximum element in a list:• maximum = foldr max -∞.

• Function prod returns the product of a list:• prod = foldr (×) 1.

• Function and returns the conjunction of a list:• and = foldr (&&) True.

• Lets emphasise again that id on lists is a fold:• id = foldr (:) [ ].

78 / 85

Page 92: [FLOLAC'14][scm] Functional Programming Using Haskell

Some Slightly Complex Folds

• length = foldr (λx n→ 1 + n) 0.

• map f = foldr (λx xs → f x : xs) [ ].

• xs ++ ys = foldr (:) ys xs. Compare this with id!

• filter p = foldr (fil p) [ ]where fil p x xs = if p x then (x : xs) else xs.

79 / 85

Page 93: [FLOLAC'14][scm] Functional Programming Using Haskell

The Ubiquitous Fold

• In fact, any function that takes a list as its input can bewritten in terms of foldr — although it might not be alwayspractical.

• With fold it comes one of the most important theorem inprogram calculation — the fold-fusion theorem. We will talkabout it later.

80 / 85

Page 94: [FLOLAC'14][scm] Functional Programming Using Haskell

Internally Labelled Binary Trees

• This is a possible definition of internally labelled binary trees:

data Tree a = Null | Node a (Tree a) (Tree a) ,

• on which we may inductively define functions:

sumT :: Tree N→ NsumT Null = 0sumT (Node x t u) = x + sumT t + sumT u .

81 / 85

Page 95: [FLOLAC'14][scm] Functional Programming Using Haskell

Exercise: given (↓) :: N→ N→ N, which yields the smaller one ofits arguments, define the following functions

1 minT :: Tree N→ N, which computes the minimal element ina tree.

2 mapT :: (a→ b)→ Tree a→ Tree b, which applies thefunctional argument to each element in a tree.

3 Can you define (↓) inductively on N? 4

4In the standard Haskell library, (↓) is called min.82 / 85

Page 96: [FLOLAC'14][scm] Functional Programming Using Haskell

• Every inductively defined datatype comes with its inductionprinciple.

• We will come back to this point later.

83 / 85

Page 97: [FLOLAC'14][scm] Functional Programming Using Haskell

Recommanded Textbooks

• Introduction to Functional Programming using Haskell. Myrecommended book. Covers equational reasoning very well.

• Programming in Haskell. A thin but complete textbook.

84 / 85

Page 98: [FLOLAC'14][scm] Functional Programming Using Haskell

Online Haskell Tutorials

• Learn You a Haskell for Great Good! , a nice tutorial withcute drawings!

• Yet Another Haskell Tutorial.

• A Gentle Introduction to Haskell by Paul Hudak, JohnPeterson, and Joseph H. Fasel: a bit old, but still worth aread.

• Real World Haskell. Freely available online. It assumes somebasic knowledge of Haskell, however.

85 / 85