0% found this document useful (0 votes)
26 views

Functional Programming PDF

Functional Programming by TyngRuey Chuang

Uploaded by

Mr.Syed
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
26 views

Functional Programming PDF

Functional Programming by TyngRuey Chuang

Uploaded by

Mr.Syed
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 21

Functional Programming

Tyng–Ruey Chuang

2010 Formosan Summer School


on Logic, Language, and Computation
June 28 – July 9, 2010

This course note . . .

• . . . is prepared for the 2010 Formosan Summer School on Logic, Language, and Com-
putation (FLOLAC) held in Taipei, Taiwan,

• . . . is made available from the FLOLAC ’10 web site:

http://flolac.iis.sinica.edu.tw/flolac10/

(please also check the above site for updated version)

• . . . and is released to the public under a Creative Commons Attribution-ShareAlike


3.0 Taiwan license:

http://creativecommons.org/licenses/by-sa/3.0/

Course outline

Unit 1. Basics of functional programming.

Unit 2. Fold/unfold functions; Parametric modules.

Each unit consists of 2 hours of lecture and 1 hour of lab/tutor. Examples will be given
in Objective Caml (O’Caml). Useful online resources about O’Caml:

• Web site: http://caml.inria.fr/

• Book: Developing Applications with Objective Caml.


URL: http://caml.inria.fr/pub/docs/oreilly-book/

1
1 Basics of functional programming
1.1 Function, evaluation, and binding
Functions
let x = 1
let y = x + 1
let succ n = n + 1
let z = succ y
• val x : int = 1
val y : int = 2
val succ : int -> int = <fun>
val z : int = 3
let sum x y = x + y
let five = sum 2 3
• val sum : int -> int -> int = <fun>
val five : int = 5
let plus3 = sum 3
let seven = plus3 4
• val plus3 : int -> int = <fun>
val seven : int = 7

Anonymous functions
let succ = fun n -> n + 1
let one = succ 0
let two = (fun n -> n + 1) one
• val succ : int -> int = <fun>
val one : int = 1
val two : int = 2
let sum = fun x -> fun y -> x + y
let plus3 = sum 3
• val sum : int -> int -> int = <fun>
val plus3 : int -> int = <fun>
let twice = fun f -> fun x -> f (f x)
let plus6 = twice plus3
let seven = plus6 one
• val twice : (’a -> ’a) -> ’a -> ’a = <fun>
val plus6 : int -> int = <fun>
val seven : int = 7

2
Functions as arguments and as results
let compose f g = fun x -> f (g x)
let plus3 n = n + 3
let times2 n = n * 2
let this = compose plus3 times2 1
let that = compose times2 plus3 1
• val compose : (’a -> ’b) -> (’c -> ’a) -> ’c -> ’b = <fun>
val plus3 : int -> int = <fun>
val times2 : int -> int = <fun>
val this : int = 5
val that : int = 8

let twice f = compose f f


let what = twice (fun n -> n + n)
let guess = what 1
• val twice : (’a -> ’a) -> ’a -> ’a = <fun>
val what : int -> int = <fun>
val guess : int = 4

Notations in O’Caml
Function application is just juxtaposition, and is left associative. These two definitions
are the same:
• let this = compose plus3 times2 1
• let this = ((compose plus3) times2) 1
Function abstraction is right associative. These two definitions are the same:
• let sum = fun x -> fun y -> x + y

val sum : int -> int -> int = <fun>

• let sum = fun x -> (fun y -> x + y)

val sum : int -> (int -> int) = <fun>

Evaluation in O’Caml
• Expressions are evaluated before they are passed as arguments to the function body.
• The function body is evaluated only when all the arguments are evaluated.
• Functions can be partially applied.

3
Binding in O’Caml

• Lexical binding: Expressions are evaluated and bound to the corresponding identifiers
in the order they appear in the program text.
• Nested binding: Outer bindings are shadowed by inner bindings.

let x = 100
let f y = let x = x + y in x
let x = 10
let z = f x

• Simultaneous binding: Several bindings occur at the same time under the same envi-
ronment.

let x = z
and z = x

• Recursive binding: Identifiers can be referred to when they are being defined.

let rec fac n = if n <= 0 then 1 else n * (fac (n -1))


let six = fac 3

Recursive functions: examples

• Expressiveness: Euclid’s algorithm for greatest common divisor (gcd), assuming inte-
gers m, n > 0:

let rec gcd m n =


if m mod n = 0
then n
else gcd n (m mod n)

let u = gcd 57 38
let v = gcd 38 59

• The danger of non-terminating computation:

let rec loop x = loop x


let oops = loop 0

4
1.2 Data types
Built-in data types in O’Caml

type int 0, -1, . . .

type char ’a’, ’\’’, . . .

type string "\"O’Caml\" is a fine language.\n", . . .

type float 3.14159, 0.314159e1, . . .

type unit = ()

type bool = false | true

type ’a list = [] | :: of ’a * ’a list [], true::false::[], [1; 2; 3], . . .

type ’a option = None | Some of ’a None, Some 17, Some [None; Some true], . . .

Built-in type operators in O’Caml

Cartesian product

type int_pair = int * int

let rec gcd (m, n) =


if m mod n = 0
then n else gcd (n, m mod n)

val gcd : int * int -> int = <fun>

Function space

type int2int2int = int -> int -> int

let rec gcd m n =


if m mod n = 0
then n else gcd n (m mod n)

val gcd : int -> int -> int = <fun>

5
Expressions, values, and types

• Well-typed expressions:

0, (1 + 2), (sum 2 3), (fun x -> fun y -> x + y), (2, true)

• Ill-typed expressions:

(1 + ’2’), (sum 2 3.0), ((fun x -> fun y -> x + y) 0 1 2)

• All O’Caml values have types:

val sum : int -> int -> int = <fun>


val five : int = 5

• Some values are polymorphic:

val twice : (’a -> ’a) -> ’a -> ’a = <fun>


val empty_list : ’a list = []

• Expressions are statically checked to ensure they always evaluate to values.

O’Caml is strict

• O’Caml insists on evaluating the arguments in a function application though the ar-
guments may not be required for the computation in the function body. O’Caml is
called a strict language.

• Some functional language, e.g., Haskell, will evaluate the function arguments only
when they are demanded by the computation in the function body. These languages
are non-strict.
• What is wrong in this picture (in O’Caml):

let oracle () = ...

let choice this that =


if oracle () then this else that

• let rec loop x = loop x


let oops = choice (loop 0) 0

6
Functions to the rescue!
let rec loop x = loop x

let choice this that =


if oracle () then this else that

let new_choice this that =


if oracle () then this () else that ()

let was = choice (loop 0) 0


let now = new_choice (fun () -> loop 0) (fun () -> 0)

val choice : ’a -> ’a -> ’a = <fun>


val new_choice : (unit -> ’a) -> (unit -> ’a) -> ’a = <fun>

What about variables?


• We can bind values to identifiers; once an identifier is bound, its value never changes.
Of course, bindings can be nested hence, for the same identifier, the inner binding may
shadow outer binding.
• Can one implement a counter using only functions?
• We can implement many counters using only functions!
• let init value = fun () -> value
let read counter = counter ()
let step counter more = fun () -> read counter + more

val init : ’a -> unit -> ’a = <fun>


val read : (unit -> ’a) -> ’a = <fun>
val step : (unit -> int) -> int -> unit -> int = <fun>

Counters via functions


let init value = fun () -> value
let read counter = counter ()
let step counter more = fun () -> read counter + more

let mem = init 0


let x = step mem 1
let y = step mem 2
let z = step x 100
let x_y_z = (read x, read y, read z)

7
val init : ’a -> unit -> ’a = <fun>
val read : (unit -> ’a) -> ’a = <fun>
val step : (unit -> int) -> int -> unit -> int = <fun>
val mem : unit -> int = <fun>
val x : unit -> int = <fun>
val y : unit -> int = <fun>
val z : unit -> int = <fun>
val x_y_z : int * int * int = (1, 2, 101)

Programming by pattern-matching

type ’a table = (string * ’a) list

let rec lookup key table =


match table with
[] -> None
| (name, value) :: rest ->
if key = name then Some value
else lookup key rest

type color = Red | Yellow | Green


let fruits = [("banana", Yellow); ("guava", Green)]

let this = lookup "guava" fruits


let that = lookup "mango" fruits

val lookup : ’a -> (’a * ’b) list -> ’b option = <fun>


val fruits : (string * color) list = [("banana", Yellow); ("guava", Green)]
val this : color option = Some Green
val that : color option = None

List reversal: two examples

• let rec reverse list =


match list with
[] -> []
| head :: tail -> (reverse tail) @ [head]

• let reverse list =


let rec rev rest accumulator =
match rest with
[] -> accumulator
| hd :: tl -> rev tl (hd :: accumulator)
in
rev list []

8
• Both have type:

val reverse : ’a list -> ’a list = <fun>

• Which one is better?

Functions over lists

let rec filter p list =


match list with
[] -> []
| head :: tail -> if p head then head :: (filter p tail)
else filter p tail

let rec append front rear =


match front with
[] -> rear
| head :: tail -> head :: (append tail rear)

let this = filter (fun n -> n mod 2 = 0) [1; 2; 3]


let that = append [1; 2; 3] [100; 101; 102]

val filter : (’a -> bool) -> ’a list -> ’a list = <fun>
val append : ’a list -> ’a list -> ’a list = <fun>
val this : int list = [2]
val that : int list = [1; 2; 3; 100; 101; 102]

User-defined type constructors

type ’a tree = Leaf


| Node of ’a * ’a tree * ’a tree

• tree is a type constructor: it construct a type α tree whenever given a type α.


• Leaf and Node are the two value constructors for type α tree.

Leaf: ’a tree
Node: ’a * ’a tree * ’a tree -> ’a tree

• In O’Caml, type constructors start with lower-case letters; value constructors start
with upper-case letters.

9
• In O’Caml, type constructors and value constructors are unary. Type construction
uses postfix notation; value construction, prefix.

Some (Node (1, Node (0, Leaf, Leaf), Node (2, Leaf, Leaf)))

has type

int tree option

Functions over trees


let rec swap tree =
match tree with
Leaf -> Leaf
| Node (here, left, right) ->
Node (here, swap right, swap left)

let rec insert key tree =


match tree with
Leaf -> Node (key, Leaf, Leaf)
| Node (here, left, right) ->
if key < here
then Node (here, insert key left, right)
else Node (here, left, insert key right)

val swap : ’a tree -> ’a tree = <fun>


val insert : ’a -> ’a tree -> ’a tree = <fun>

Functions over trees, continued


let rec build f s =
match f s with
None -> Leaf
| Some (a, left, right) ->
Node (a, build f left, build f right)

let range (low, high) =


if low > high
then None
else let mid = (low + high) / 2 in
Some (mid, (low, mid - 1), (mid + 1, high))

let tree1to7 = build range (1, 7)

10
val build : (’a -> (’b * ’a * ’a) option) -> ’a -> ’b tree = <fun>
val range : int * int -> (int * (int * int) * (int * int)) option = <fun>
val tree1to7 : int tree = Node (4, Node (2, Node (1, Leaf, Leaf), Node (3, Leaf, Leaf)),
Node (6, Node (5, Leaf, Leaf), Node (7, Leaf, Leaf)))

2 Fold/unfold functions; Parametric modules


2.1 Fold/unfold functions for data types
Functions over lists, re-visited
let rec filter p list =
match list with
[] -> []
| head :: tail ->
if p head then head :: (filter p tail)
else filter p tail

let rec append front rear =


match front with
[] -> rear
| head :: tail -> head :: (append tail rear)

• Both functions work on lists in a bottom-up manner.


• What is the base case, and what is the inductive step?

Fold function for lists


let rec fold (base, step) list =
match list with
[] -> base
| hd :: tl -> step (hd, fold (base, step) tl)

let filter p list =


let step (hd, acc) = if p hd then (hd :: acc) else acc
in
fold ([], step) list

let append front rear =


fold (rear, fun (hd, acc) -> hd :: acc) front

val fold : ’a * (’b * ’a -> ’a) -> ’b list -> ’a = <fun>


val filter : (’a -> bool) -> ’a list -> ’a list = <fun>
val append : ’a list -> ’a list -> ’a list = <fun>

11
Fold function for trees
let rec swap tree =
match tree with
Leaf -> Leaf
| Node (here, left, right) ->
Node (here, swap right, swap left)

let rec fold (base, step) tree =


match tree with
Leaf -> base
| Node (here, left, right) ->
step (here, fold (base, step) left,
fold (base, step) right)

let swap’ tree = fold (Leaf,


fun (here, left, right) -> Node (here, right, left)) tree

val fold : ’b * (’a * ’b * ’b -> ’b) -> ’a tree -> ’b = <fun>

What is a tree, anyway?


fold : ’b * (’a * ’b * ’b -> ’b) -> ’a tree -> ’b
• A tree of type α tree is a value that can be folded.
• Whenever given a base value of type β, and an inductive function of type α×β ×β → β,
a tree can be folded into a value of type β.

A new data type for trees


type (’a, ’b) t = Leaf
| Node of ’a * ’b * ’b

type ’a tree = Rec of (’a, ’a tree) t

let rec fold f tree =


match tree with
Rec Leaf -> f Leaf
| Rec (Node (here, left, right)) ->
f (Node (here, fold f left, fold f right))

type (’a, ’b) t = Leaf | Node of ’a * ’b * ’b


type ’a tree = Rec of (’a, ’a tree) t
val fold : ((’a, ’b) t -> ’b) -> ’a tree -> ’b = <fun>

12
A new swap function
let swap tree =
let f t = match t with Leaf -> Rec Leaf
| Node (here, left, right) ->
Rec (Node (here, right, left))
in
fold f tree

let tree123 = Rec (Node (2, Rec (Node (1, Rec Leaf, Rec Leaf)),
Rec (Node (1, Rec Leaf, Rec Leaf))))
let tree321 = swap tree123

val swap : ’a tree -> ’a tree = <fun>


val tree123 : int tree =
Rec (Node (2, Rec (Node (1, Rec Leaf, Rec Leaf)),
Rec (Node (1, Rec Leaf, Rec Leaf))))
val tree321 : int tree =
Rec (Node (2, Rec (Node (1, Rec Leaf, Rec Leaf)),
Rec (Node (1, Rec Leaf, Rec Leaf))))

Look at a tree this way!


type (’a, ’b) t = Leaf | Node of ’a * ’b * ’b

type ’a tree = Rec of (’a, ’a tree) t

val fold : ((’a, ’b) t -> ’b) -> ’a tree -> ’b = <fun>
• Type constructor (α, β) t defines (the only) two forms of a tree node.
• Type constructor α tree defines a tree as a recursive structure via type constructor
(α, β) t. The recursion occurs at the second type argument to t.
• A function of type (α, β) t → β comprises both the base case and the inductive step
necessary for folding a value of type α tree to a value of type β.

A new data type for trees, continued


type (’a, ’b) t = Leaf
| Node of ’a * ’b * ’b

type ’a tree = Rec of (’a, ’a tree) t

let rec unfold g seed =


match g seed with
Leaf -> Rec Leaf
| Node (here, left, right) ->
Rec (Node (here, unfold g left, unfold g right))

13
type (’a, ’b) t = Leaf | Node of ’a * ’b * ’b
type ’a tree = Rec of (’a, ’a tree) t
val unfold : (’a -> (’b, ’a) t) -> ’a -> ’b tree = <fun>

We saw this before!

let rec build f s =


match f s with
None -> Leaf
| Some (a, left, right) ->
Node (a, build f left, build f right)

let range (low, high) =


if low > high
then None
else let mid = (low + high) / 2 in
Some (mid, (low, mid - 1), (mid + 1, high))

let tree1to7 = build range (1, 7)

val build : (’a -> (’b * ’a * ’a) option) -> ’a -> ’b tree = <fun>
val range : int * int -> (int * (int * int) * (int * int)) option = <fun>
val tree1to7 : int tree = Node (4, Node (2, Node (1, Leaf, Leaf), Node (3, Leaf, Leaf)),
Node (6, Node (5, Leaf, Leaf), Node (7, Leaf, Leaf)))

Rewrite it using unfold

let rec unfold g seed =


match g seed with
Leaf -> Rec Leaf
| Node (here, left, right) ->
Rec (Node (here, unfold g left, unfold g right))

let range (low, high) =


if low > high
then Leaf
else let mid = (low + high) / 2 in
Node (mid, (low, mid - 1), (mid + 1, high))

let balanced = unfold range


let tree1to7 = balanced (1, 7)

val unfold : (’a -> (’b, ’a) t) -> ’a -> ’b tree = <fun>
val range : int * int -> (int, int * int) t = <fun>
val balanced : int * int -> int tree = <fun>
val tree1to7 : ...

14
Look at a tree the other way!
type (’a, ’b) t = Leaf | Node of ’a * ’b * ’b

type ’a tree = Rec of (’a, ’a tree) t

val unfold : (’b -> (’a, ’b) t) -> ’b -> ’a tree = <fun>
• Type constructor (α, β) t defines (the only) two forms of a tree node.
• Type constructor α tree defines a tree as a recursive structure via type constructor
(α, β) t. The recursion occurs at the second type argument to t.
• A function of type β → (α, β) t comprises the co-inductive step necessary for unfolding
a value of type β to a value of type α tree.

Fold and unfold for trees


let rec fold f tree =
match tree with
Rec Leaf -> f Leaf
| Rec (Node (here, left, right)) ->
f (Node (here, fold f left, fold f right))

let rec unfold g seed =


match g seed with
Leaf -> Rec Leaf
| Node (here, left, right) ->
Rec (Node (here, unfold g left, unfold g right))
val fold : ((’a, ’b) t -> ’b) -> ’a tree -> ’b = <fun>
val unfold : (’a -> (’b, ’a) t) -> ’a -> ’b tree = <fun>

Functions fold and unfold look strangely similar to each other!

Fold and unfold for trees, the third round (I)


type (’a, ’b) t = Leaf
| Node of ’a * ’b * ’b

let map (f, g) t =


match t with Leaf -> Leaf
| Node (h, l, r) -> Node (f h, g l, g r)

type ’a tree = Rec of (’a, ’a tree) t

let down (Rec t) = t


let up t = Rec t

15
val map : (’a->’b) * (’c->’d) -> (’a,’c) t -> (’b,’d) t = <fun>
val down : ’a tree -> (’a, ’a tree) t = <fun>
val up : (’a, ’a tree) t -> ’a tree = <fun>

Fold and unfold for trees, the third round (II)

type (’a, ’b) t = Leaf


| Node of ’a * ’b * ’b

type ’a tree = Rec of (’a, ’a tree) t

let id x = x

let rec fold f tree = f (map (id, fold f) (down tree))

let rec unfold g seed = up (map (id, unfold g) ( g seed))

val id : ’a -> ’a = <fun>


val fold : ((’a, ’b) t -> ’b) -> ’a tree -> ’b = <fun>
val unfold : (’a -> (’b, ’a) t) -> ’a -> ’b tree = <fun>

Fold and unfold for trees — ever more functional!


Fold and unfold are functions that each takes in a (basis) function as the argument and
return a (tree) function as the result.

let ($) f g x = f (g x)

let rec fold f tree = (f $ map (id, fold f) $ down) tree


let rec unfold g seed = (up $ map (id, unfold g) $ g) seed

let this = fold up


let that = unfold down

val ( $ ) : (’a -> ’b) -> (’c -> ’a) -> ’c -> ’b = <fun>
val fold : ((’a, ’b) t -> ’b) -> ’a tree -> ’b = <fun>
val unfold : (’a -> (’b, ’a) t) -> ’a -> ’b tree = <fun>
val this : ’a tree -> ’a tree = <fun>
val that : ’a tree -> ’a tree = <fun>

What is this, and what is that?

16
Functional diagram for fold
In the diagram, functions are arrows, and types are objects.

down -
α tree (α, α tree) t

fold f map (id, fold f )


? f ?
β (α, β) t

let rec fold f tree = (f $ map (id, fold f) $ down) tree

Functional diagram for unfold


In the diagram, functions are arrows, and types are objects.

g
β - (α, β) t

unfold f map (id, unfold f )


? up ?
α tree  (α, α tree) t

let rec unfold g seed = (up $ map (id, unfold g) $ g) seed

Let’s not forget lists!

type (’a, ’b) t = Null


| Cons of ’a * ’b

type ’a list = Rec of (’a, ’a list) t

let rec fold f list = (f $ map (id, fold f) $ down) list


let rec unfold g seed = (up $ map (id, unfold g) $ g) seed

down g
α list - (α, α list) t β - (α, β) t

fold f map (id, fold f ) unfold f map (id, unfold f )


? f ? ? up ?
β (α, β) t α list  (α, α list) t

17
2.2 Parametric Modules
Modules

• A module, also called structure, packs together related definitions (types, values, and
even modules).

• The module name acts as a “name space” to avoid name conflicts.

module MyStack =
struct
type ’a t = ’a list
let empty = []
let push elm stack = elm :: stack
let pop stack =
match stack with
[] -> None
| head :: tail -> Some (head, tail)
end

let whatever = MyStack.push 1 []

Module interfaces

• A module interface, also called signature, specifies which components of a structure


are accessible from the outside, and with which type.

• It acts as a contract between the user and the implementer of a module. Interface
checking is always enforced in O’Caml.

module type STACK =


sig
type ’a t
val empty: ’a t
val push: ’a -> ’a t -> ’a t
val pop: ’a t -> (’a * ’a t) option
end

module S: STACK = MyStack


let whatever = S.push 1 S.empty

Parametric modules

• A parametric module, also called functor, is a structure parameterized by other struc-


tures.

18
• Type sharing and structure sharing constraints can be used to relate the arguments
and the result.

module type QUEUE = STACK


module type S2Q = functor (S: STACK) -> QUEUE

module MakeQueue: S2Q = functor (S: STACK) ->


struct
type ’a t = ’a S.t * ’a S.t
let empty = (S.empty, S.empty)
let push elm (front, rear) = (front, S.push elm rear)
let pop (front, rear) =
match S.pop front with
Some (e, s) -> Some (e, (s, rear))
| None -> ...
end

Tree folding

type (’a, ’b) t = Leaf


| Node of ’a * ’b * ’b

let map (f, g) t =


match t with Leaf -> Leaf
| Node (h, l, r) -> Node (f h, g l, g r)

type ’a tree = Rec of (’a, ’a tree) t

down -
α tree (α, α tree) t

fold f map (id, fold f )


? f ?
β (α, β) t

List folding

type (’a, ’b) t = Null


| Cons of ’a * ’b

let map (f, g) t =


match t with Null -> Null

19
| Cons (hd, tl) -> Cons (f hd, g tl)

type ’a list = Rec of (’a, ’a list) t

down -
α list (α, α list) t

fold f map (id, fold f )


? f ?
β (α, β) t

A fold for all seasons?

• Wanted: A way to describe the derivation of a unary type constructor by recursing


over a binary type constructor, and to define the accompanying fold function at the
same time.

• This is exactly what a parametric module can do!

• Input: a module with a binary type constructor and its map function.

• Output: a module with a unary type constructor, its map function, and its fold and
unfold functions.

Module interfaces FUN and FIX

module type FUN =


sig
type (’a, ’u) t
val map: (’a -> ’b) * (’u -> ’v) -> (’a, ’u) t -> (’b, ’v) t
end

module type FIX =


sig
module Base: FUN
type ’a t = Rec of (’a, ’a t) Base.t
val down: ’a t -> (’a, ’a t) Base.t
val up: (’a, ’a t) Base.t -> ’a t

val map: (’a -> ’b) -> ’a t -> ’b t


val fold: ((’a, ’x) Base.t -> ’x) -> ’a t -> ’x
end

20
Mu, the fixed-pointing module

module type MU = functor (B: FUN) -> FIX with module Base = B

module Mu: MU = functor (B: FUN) ->


struct
module Base = B
type ’a t = Rec of (’a, ’a t) Base.t
let down (Rec t) = t
let up t = Rec t

let rec fold f (Rec t) = f (Base.map (id, fold f) t)


let rec map f (Rec t) = Rec (Base.map (f, map f) t)
end

Module Tree

module T =
struct
type (’a, ’b) t = Leaf
| Node of ’a * ’b * ’b

let map (f, g) t =


match t with Leaf -> Leaf
| Node ( h, l, r) ->
Node (f h, g l, g r)
end

module Tree = Mu(T)

Module List

module L =
struct
type (’a, ’b) t = Null
| Cons of ’a * ’b
let map (f, g) t =
match t with Null -> Null
| Cons ( hd, tl) ->
Cons (f hd, g tl)
end

module List = Mu(L)

21

You might also like