CS 5205 Foundation in Programming Languages Basics of
CS 5205: Foundation in Programming Languages Basics of Functional Programming. CS 5205 Haskell 1
Topics • • Higher-Order Functions Formal Reasoning Abstraction vs Efficiency Bridging the Divide CS 5205 Haskell 2
Function Abstraction • Function abstraction is the ability to convert any expression into a function that is evaluated at a later time. p = () -> Expr <Expr> time p () time Normal Execution CS 5205 Delayed Execution Haskell 3
Higher-Order Functions • Higher-order programming treats functions as first-class, allowing them to be passed as parameters, returned as results or stored into data structures. • This concept supports generic coding, and allows programming to be carried out at a more abstract level. • Genericity can be applied to a function by letting specific operation/value in the function body to become parameters. CS 5205 Haskell 4
Genericity • Replace specific entities (0 and +) by parameters. sum. List ls = case ls of [] -> 0 x: xs -> x+(sum. List xs) foldr f u ls = case ls of [] -> u x: xs -> f x (foldr f u xs) CS 5205 Haskell 5
Polymorphic, Higher-Order Types sum. List : : [Int] -> Int sum. List : : Num a => [a] -> a foldr : : (a -> b) -> b -> [a] -> b CS 5205 Haskell 6
Instantiating Generic Functions sum. L 2 : : Num a => [a] -> a sum. L 2 ls = foldr (+) 0 ls sum. L 2 [1, 2, 3] ) sum. L 2 [1. 1, 3, 2. 3] ) CS 5205 Haskell 7
Instantiating Generic Functions prod. L : : Num a => [a] -> a prod. L ls = foldr (*) 1 ls prod. L [1, 2, 5] ) prod. L [1. 1, 3, 2. 3] ) CS 5205 Haskell 8
Instantiating Generic Functions • Can you express map in terms of foldr? map : : (a -> b) -> [a] -> [b] map f [] = [] map f (x: xs) = (f x) : (map f xs) map f xs CS 5205 = foldr … … … xs Haskell 9
Instantiating Generic Functions • Filtering a list of elements with a predicate. filter : : (a -> filter f [] = filter f (x: xs) if (f x) then else filter f Bool) -> [a] [] = x : (filter f xs) xs • Can we express filter in terms of foldr? filter f xs CS 5205 = foldr … … … xs Haskell 10
Pipe/Compose compose : : (b -> c) -> (a -> b) -> a -> c compose f g = x -> f (g x) g | f = compose f g • Similar to Unix pipe command: cmd 1 CS 5205 | cmd 2 Haskell 11
Iterator Construct for : : Int -> (Int -> a) -> a for i j f a = if i>j then a else for (i+1) j (f i a) • In Haskell, type class help give a more generic type: for : : Num b, Ord b => b -> (b -> a) -> a CS 5205 Haskell 12
Right Folding foldr f u [x 1, x 2, . . , xn] Þ f x 1 (foldr f u [x 2. . xn]) Þ f x 1 (f x 2 (fold f u [x 3. . xn])) Þ f x 1 (f x 2 (… (fold f u [xn]) …)) Þ f x 1 (f x 2 (… (f xn u) …))) associate to right CS 5205 Haskell 13
Left Folding – Tail Recursion • Accumulate result in a parameter: foldl f u ls = case ls of [] -> u x: xs -> foldl f (f u x) xs based on accumulation • What is the type of foldl? • Can we compute factorial using it? CS 5205 Haskell 14
Left Folding foldl f u [x 1, x 2, . . , xn] Þ foldl f (f u x 1) [x 2. . xn] Þ foldl f (f (f u x 1) x 2) [x 3. . xn])) Þ foldl f (f … (f (f u x 1) x 2)… xn) [] Þ f (… (f (f u x 1) x 2) …) xn left is here! CS 5205 Haskell 15
Instance of Left Folding • Summing a list by accumulation. sum. T acc ls = case ls of [] -> 0 x: xs -> sum. T (x+acc) xs sum. List ls = sum. T 0 ls sum. T acc ls = foldl (+) acc ls CS 5205 Haskell 16
Referential Transparency • An expression is referentially transparent if it can always be replaced by an equivalent expression with the same value and effect. Allows reasoning based on components. • Useful for: • simplifying algorithm • proving correctness • optimization + parallelization • Pure functions are referentially transparent, as relied on in mathematical reasoning. CS 5205 Haskell 17
Equivalence Proof • Can we Prove : sum. List xs = sum. T 0 xs. • Generalise : (sum. List xs)+a = sum. T a xs. • By Induction Case : x=[] (sum. List [])+a = sum. T a [] 0+a = a Case : x=x: xs (sum. List x: xs)+a = sum. T a (x: xs) x+(sum. List xs)+a = sum. T (x+a) xs (sum. List xs)+(x+a) = sum. T (x+a) xs // apply induction hypothesis CS 5205 Haskell 18
List Reversal • Concatenate first element to last position. rev [] rev (x: xs) = [] = rev xs ++ [x] rev xs = foldr … … … What is the time complexity? CS 5205 Haskell 19
Time Complexity • Assume : C(xs++ys) = length xs Derive : Steps(rev(xs)) Steps • Case [] : Steps(rev([])) = 1+Steps([]) Steps = 1+0 • Case x: xs : Steps(rev(x: xs)) Steps = 1+Steps(rev(xs)++[a]) Steps = 1+C(rev(xs)++_)+Steps(rev(xs)) Steps = 1+length(rev(xs)+Steps(rev(xs)) Steps = 1+length(xs)+Steps(rev(xs)) Steps • Thus : C(rev(xs)) CS 5205 = (length xs)^2 Haskell 20
Iterative List Reversal • Concatenate first element to last position. rev. T w [] rev. T w (x: xs) = w = rev. T (x: w) xs Same as: rev. T w xs = foldl ( w x -> x: w) w xs What is the time complexity? CS 5205 Haskell 21
Time Complexity • Derive Steps(rev. T w xs) Steps • Case [] : Steps(rev. T w []) Steps = 1+Steps(w) Steps = 1+0 • Case x: xs : Steps(rev. T w (x: xs)) Steps = 1+Steps(rev. T (x: w) xs) Steps = 1+Steps(rev. T _ xs) Steps • Thus : C(rev. T w xs) CS 5205 = (length xs) Haskell 22
Abstraction vs Efficiency • Abstraction helps with programmers’ productivity • Efficiency helps machine execution. • Tension between abstraction and efficiency • Abstract program • stress on ‘what’ rather than ‘how’ • typically uses simpler (maybe naïve) algorithm • Efficient program • optimised implementation • use of clever programming techniques CS 5205 Haskell 23
Bridging the Divide Abstract Code/Specs transform or synthesize verify Efficient Code or Implementation CS 5205 Haskell 24
Unfold/Fold Transformation • DEFINE - new function definition • UNFOLD – replace a call by its body • FOLD – replace an expression matching the RHS of a definition by its corresponding call • INSTANTIATE – provide special cases of a given equation. • ABSTRACT – introduce a tuple of expressions • LAW – application of valid lemma, e. g. associativity CS 5205 Haskell 25
Fusion Transformation • Consider: …sum (double xs)… sum [] = 0 sum x: xs = x+(sum xs) double [] = [] double x: xs = 2*x : (double xs) • Computation reuses smaller functions to build larger ones but may result in unnecessary intermediate structures. They can cause space overheads. • Solution : Fuse the code together! CS 5205 Haskell 26
Fusion Transformation • Define: sumdb xs = sum (double xs) • Instantiate: xs=[] sumdb [] = sum (double []) = sum [] = 0 • Instantiate: xs=x: xs sumdb x: xs = = CS 5205 sum 2*x (double x: xs) (2*x : double xs) + sum(double xs) + (sumdb xs) Haskell 27
Iteration Transformation • Define: sumdb. T a xs = a+(sumdb xs) • Instantiate: xs=[] sumdb. T a [] = a+(sumdb []) = a • Instantiate: xs=x: xs sumdb. T a (x: xs) CS 5205 = = a+(sumdb x: xs) a+(2*x + sumdb xs) (a+2*x) + (sumdb xs) sumdb. T (a+2*x) xs Haskell 28
Laziness vs Strictness • Laziness increase expressiveness, allowing infinite data structures, by not evaluating each subexpression until it is really needed. • However, there is a performance penalty (both space and time), as the suspended computation has to be stored as a closure and then invoked subsequently. • If you always need some of the parameters, we might as well evaluate their corresponding arguments first. • Question : When is an argument to a function needed (strict)? Using ? to denote non-termination. f…? … = ? CS 5205 Haskell 29
Strictness Transformation • Can analyse that accumulating argument of sumdb is strict. We can force strictness using the `seq` operator. sumdb. T a [] sumdb. T a (x: xs) = a = sumdb. T (a+2*x) xs sumdb. T a [] sumdb. T a (x: xs) = a = let p = a+2*x in p `seq` sumdb. T p xs CS 5205 Haskell 30
Tupling Transformation • Average of a List : average xs = sum xs / length xs • Define : both xs = (sum xs, length xs) • Instantiate : both [] = (sum [], length []) = (0, 0) • Instantiate : both x: xs = (sum x: xs, length x: xs) = (x+sum xs, 1+length xs) = let (u, v)= (sum xs, length xs) in (x+u, 1+v) = let (u, v)= both xs in u/v CS 5205 Haskell 31
Naive Fibonacci • Natural but inefficient version of fibonacci : fib 0 = 1 fib 1 = 1 fib n = (fib n-1)+(fib n-2) • Time complexity is exponential time due to presence of redundant calls. CS 5205 Haskell 32
A Call Tree of fib 6 fib 4 fib 5 fib 4 fib 3 fib 2 fib 3 fib 1 fib 2 fib 1 Many repeated calls! CS 5205 Haskell 33
A Call Graph of fib 6 fib 5 fib 4 fib 3 fib 2 fib 1 No repeated call through reuse of identical calls CS 5205 Haskell 34
Tupling - Computing Two Results Compute two calls from next two calls in hierarchy: ((fib n) , (fib n-1)) ((fib n-1) , (fib n-2)) CS 5205 Haskell 35
Tupling Fibonacci • Define : fibtup n = (fib n+1, fib n) • Instantiate : fibtup 0 = (fib 1, fib 0) = (1, 1) • Instantiate : fibtup n+1 = (fib n+2, fib n+1) = ((fib n+1)+(fib n), fib n+1) = let (u, v)= (fib n+1, fib n) in (u+v, u) = let (u, v)= fibtup n in (u+v, u) CS 5205 Haskell 36
Using the Tupled Function fib 0 = 1 fib 1 = 1 fib n = fib n-1 + fib n-2 = let in CS 5205 (u, v) = (fib n-1, fib n-2) u+v (u, v) = fibtup (n-2) u+v Haskell 37
Linear Recursion fib 6 fib 5 fib 4 fib 3 fibtup 0 = (1, 1) fibtup (n+1) = let (u, v)= fibtup n in (u+v, u) fib 2 fib 1 CS 5205 Haskell 38
To Iteration fib 6 fib 5 fib 4 fib 3 fib. T 0 u v = (u, v) fib. T (n+1) u v = fib. T n (u+v, u) fib 2 fib 1 CS 5205 Haskell 39
Optimization • Should be done only if critical. • Should be automated where possible, for e, g. as part of compilation. • Manual optimization by human should preferably be carefully checked or verified CS 5205 Haskell 40
- Slides: 40