CS 541 Lecture -*- Outline -*- * Reasoning about functional programs (this uses Haskell list and type notation) ** Basic tools *** equational reasoning This is the main tool, used all the time but it's just substituting equals for equals. We prefer a calculational style for this: ------------------------------------------ CALCULATIONAL STYLE Basic format for proving equations X = < hint why X = Y > Y = < hint why Y = Z > Z This really means that X = Y and Y = Z, from which X = Z follows. ------------------------------------------ ------------------------------------------ EXAMPLE CALCULATION Suppose we have > fst x y = x > snd x y = y > cons x y f = f x y > car z = z fst > cdr z = z snd Then car (cdr (cons 3 (cons 1 2))) = (cdr (cons 3 (cons 1 2))) fst = (cons 3 (cons 1 2) snd) fst = (snd 3 (cons 1 2)) fst = cons 1 2 fst = fst 1 2 = 1 ------------------------------------------ Some tips: start with the more complex side, make each step small work from the other end also if that helps *** structural induction **** for finite lists ------------------------------------------ STRUCTURAL INDUCTION FOR FINITE LISTS General form To prove: for all l::[a], P(l) holds. Technique: Exercise: append [] bs = bs append (a:as) bs = a:(append as bs) prove for all cs :: [a] (append cs []) = cs ------------------------------------------ ... basis: prove P([]) inductive step: prove for all c and cs, P(cs) implies P(c:cs) Q: can you do the proof? Pf: basis: append [] [] = { by def of append } [] inductive step: assume (append cs []) = cs append (c:cs) [] = { by def of append } c:(append cs []) = { inductive hyp } c:cs QED **** for trees ------------------------------------------ STRUCTURAL INDUCTION FOR TREES > data Tree a = Lf > | Br a (Tree a) (Tree a) What is needed to prove for all t::(Tree a), P(t)? ------------------------------------------ ... must prove: P(Lf) and for all a, bt, and ct, P(bt) && P(ct) ==> P(Br a bt ct) Example: prove that all trees have an odd number of nodes, > nodeCount Lf = 1 > nodeCount (Br a bt ct) = 1 + nodeCount bt + nodeCount ct To prove for all t:: (Tree a), odd(nodeCount t) Basis: odd(nodeCount Lf) = < def of nodeCount > odd(1) = < def of odd > True inductive step: assume odd(nodeCount bt) and odd(nodeCount ct). odd(nodeCount (Br a bt ct)) = < def of nodeCount > odd(1 + nodeCount bt + nodeCount ct) = < by the inductive hpothesis, nodeCount bt and nodeCount ct are odd, and thus their sum is even > True **** generalizing Q: how to describe structural induction in general? primitive constructors of type t: constructors that take no argument of type t e.g., [], Lf nonprimitive constructors: constructors that take at least one argument of type t e.g., (:) and Br To prove: basis: P holds for primitive constructors inductive step: if P(a) then P(non-prim(a)) ** partial and infinite lists, laziness (Thompson 17.9) *** finite, partial lists ------------------------------------------ FINITE PARTIAL LISTS def: A finite-partial list of type [t] is either: [], -- the empty list _|_, -- a nonterminating computation -- of type [t], or (a:as), where a::t and as::[t] So to prove P(x) for all x::[t], ------------------------------------------ ... must show (a) P([]) (b) P(_|_) holds, _|_ is a nonterminating compuation of type [t] (c) P(as) ==> P(a:as) holds for all a:t and as::[t] example (Thompson p. 377): To show: for all xs:: [Integer], sum (map (2*) xs) = 2 * sum xs Basis (a): sum (map (2*) []) = < def of map > sum [] = < def of sum > 0 = < arithmetic > 2 * 0 = < def of sum > 2 * sum [] Basis (b): Let undef be a non-terminating computation sum (map (2*) undef) = < def of map > sum undef = < def of sum > undef = < multiplication is strict > 2 * undef = < def of sum > 2 * sum undef Inductive step (c): assume sum (map (2*) xs) = 2 * sum xs sum (map (2*) (x:xs)) = < def of map > sum ((2*) x : (map (2*) xs)) = < def of section (2*)> sum ( 2 * x : (map (2*) xs) = 2 * x + sum (map (2*) xs) = < inductive hypothesis > 2 * x + 2 * sum xs = < arithmetic > 2 * (x + sum xs) = < def of sum > 2 * sum (x:xs) *** infinite lists See also Bird "Introduction to FP using Haskell", section 9.1.1 ------------------------------------------ INFINITE LISTS To prove P(xs) holds for all infinite lists of type [t]: for all natural numbers n, xs!!n Example: Each element of the list > plus4s = [1, 5 ..] is odd. Proof: by induction Basis: (n = 0) odd(plus4s!!0) = < def of plus4s > odd([1, 5 ..]!!0) = < def of !!> odd(1) = < def of odd> True Inductive step. Let n >= 0. Assume odd(plus4s!!n) odd(plus4s!!(n+1)) = < def of plus4s > odd([1, 5 ..]!!(n+1)) = < def of !! and Haskell notation> odd(plus4s!!n + 4) = < by the inductive hypothesis, plus4s!!n is odd, and by arithmetic add 4 to an odd number is odd> True ------------------------------------------ See Thompson p. 379 for another example ------------------------------------------ PROVING EQUATIONS ABOUT INFINITE LISTS An equation between lists is valid if it is valid for all fp-lists. Reason: the set of fp-lists include all approximations of the infinite lists. Example: Prove, for all functions f, g and for all lists xs, (map f . map g) xs = map (f.g) xs ------------------------------------------ Basis (a): (map f . map g) [] = < def of (.) > map f (map g []) = < def of map > map f [] = < def of map > [] = < def of map > map (f.g) [] Basis (b): Let undef be a non-terminating computation (map f . map g) undef = < def of (.) > map f (map g undef) = < def of map > map f undef = < def of map > undef = < def of map > map (f.g) undef Inductive step (c): (map f . map g) xs = map (f.g) xs (map f . map g) (x:xs) = < def of (.) > map f (map g (x:xs)) = < def of map > map f (g x:map g xs) = < def of map > f (g x) : map f (map g xs) = < inductive hypothesis > f (g x) : map (f.g) xs = < def of (.) > (f.g) x : map (f.g) xs = < def of map > map (f.g) (x:xs)