Post on 17-Dec-2015
Functional Design and Programming
Lecture 11:
Functional reasoning
Literature
Paulson, chapter 6
Exercises
Paulson: 6.5-6.8, 6.9, 6.12
Overview
Induction principles Program verification Transformational programming
Logical Notation: Formulas
¬ A not A A B A and B A B A or B A -> B A implies B A <-> B A if and only if B x. A(x) for all x, A(x) x. A(x) for some x, A(x)
Free and Bound Variables
All variable occurrences in the scope of a or -binding are bound; e.g. n and m inm n. m > n.
All other variable occurrences in a formula are free.
Bound-variable renaming
Bound variables (variable binding plus all associated bound variable occurrences) can be renamed without changing the meaning of the formula.
Example: m n. m > n. q p. q > p. m’ n’. m’ > n’.
Precedence of Connectives
The logical connectives bind weaker and weaker in the order given before.
Natural Numbers
Basis: (n = 0) 0 is a natural number. Rule: (n -> n+1) If n is a natural number,
then n+1 is a natural number. Nothing else is a natural number.
0
basis
1
rule
2
rule
3 4 5 6 7 ...
rule rule ...
Mathematical Induction
We want to prove n N. P(n) by mathematical induction on n.
Basis: (n = 0) Prove P(0).
Induction: (n = m +1) Assume that P(m) holds. (Induction hypothesis) Show that P(m+1) holds.
Mathematical Induction...
0
basis
P(0)
1
rule
P(1)
3 4 5 6 7 ...
rule rule ...
P(3) P(4) P(5) P(6) P(7) ...
2
P(2)
rule
Example: Factorials
fun fact 0 = 1 | fact n = n * fact (n-1)
fun facti (0, p) = p | facti (n, p) = facti (n-1, n*p)
Example: Fibonacchi Numbers
fun fib 0 = 0 | fib 1 = 1 | fib n = fib (n-1) + fib (n-2)
fun itfib (1, prev, curr) = curr | itfib (n, prev, curr) = itfib (n-1, curr, prev + curr)
Complete Induction
We want to prove nN. P(n) by complete induction on n.
Induction: Assume that k < n. P(k) holds. (Induction
hypothesis) Show that P(n) holds.
Complete Induction...
0
P(0)
1
P(1)
3 4 5 6 7 ...
P(3) P(4) P(5) P(6) P(7) ...
2
P(2)
We may assume that P(0),..., P(n-1)have already been proved when proving P(n).(Consider n=7 above.)
Structural Induction: Lists
We want to prove la list. P(l) by structural induction on l.
Basis: (l = nil) Prove P(nil).
Induction: (l = x :: xs) Assume that P(xs) holds. (Induction hypothesis) Show that P(x :: xs) holds.
List functions
fun nlength nil = 0 | nlength (x::xs) = 1 + nlength xs
fun nil @ ys = ys | (x :: xs) @ ys = x :: (xs @ ys)
fun nrev nil = nil | nrev (x::xs) = (nrev xs) @ [x]
fun revAppend (nil, ys) = ys | revAppend (x::xs, ys) = revAppend (xs, x::ys)
Structural Induction: Trees
We want to prove t a tree. P(t) by structural induction on t.
Basis: (t = Lf) Prove P(Lf).
Induction: (t = Br(x, t1, t2)) Assume that P(t1) and P(t2) hold. (Induction
hypotheses) Show that P(Br(x, t1, t2)) holds.
Tree functions
fun size Lf = 0 | size (Br (v, t1, t2)) = 1 + size t1 + size t2
fun depth Lf = 0 | depth (Br (v, t1, t2)) = 1 + Int.max (depth t1, depth t2)
Transformational Programming
Goal: Transforming specifications into programs that are guaranteed to be correct by construction.
Method: Apply transformation rules on functional programs (specifications) that are guaranteed to preserve their semantics (correctness).
Transformational Programming: Example
fun preorder Lf = nil | preorder (Br (x, t1, t2)) = [x] @ preorder t1 @ preorder t2
Idea: Find function preorder’ such that
preorder’(t, l) = preorder t @ l
for all trees t and lists l.
(Formal) Program Verification
Specification: Precise description of result of program execution.
Verification: Did we build the system right? Proof or test that a program satisfies the given specification.
Validation: Did we build the right system? Test that program has the intended behavior.
(Formal) Program Verification...
Ingredients: Program: The program at hand Specification: What properties should the
program satisfy to be considered correct? Assumptions: What properties about the
programming language, hardware, context, etc., are assumed to hold?
Input/Output Relations
Many specifications can be expressed as input/output (I/O) predicates together with a domain of inputs for which the predicate must be satisfied.
Example: Predicate: P(t, l) <=> l = preorder t Domain: a tree (all a trees, for arbitrary a)
Total Program Correctness
A functional program f is totally correct wrt. I/O specification (P, X) if: for all x in X, P(x, f(x))
Example: for all t in a tree, preorder’ (t, nil) = preorder t
Partial Program Correctness
A functional program f is partially correct wrt. I/O specification (P, X) if: for all x in X, if f(x) terminates then P(x, f(x)).
Example: for all t in a tree, if undef(t) terminates then
undef(t) = preorder t where fun undef (t: a tree) = undef t.
Program Correctness: Sorting
Correctness predicate and domain: sorted(l, l’) <=>
ordered(l’)bag(l) = bag(l’)
t list, where t is totally ordered by operation <=: t * t -> bool
Example: forall l in t list, sorted(l, tmergesort l).
Sorting...
fun ordered nil = true | ordered [x] = true | ordered (x::y::ys) = x <= y andalso ordered (y::ys)
bag(l) = the multiset consisting of all the elements in list l.
Sorting...
fun split k nil = (nil, nil) | split 0 l = (nil, l) | split k (x :: xs) = let val (l1, l2) = split (k-1) xs in (x :: l1, l2) end
Sorting...
fun merge (nil, ys) = ys | merge (xs, nil) = xs | merge (x::xs, y::ys) = if x <= y then x :: merge(xs, y::ys) else y :: merge(x::xs, ys)
Sorting...
fun mergesort nil = nil | mergesort [x] = [x] | mergesort xs = let val k = length xs div 2 val (l1, l2) = split k xs in merge (mergesort l1, mergesort l2) end
Value of Specification
Specification requires making requirements explicit (and thus thinking about the job, before beginning with something or other)
Specifications may pinpoint: ambiguities inconsistencies incompleteness
Limitations of Specification
Not all problems can be specified completely and rigorously (e.g., where specification and validation interact)
Specifications may be counterproductive (e.g., premature or excessive formalization, not human readable and/or with no tool support)
Specification may be wrong or incomplete.
Value of Verification
Verification of simple properties can pinpoint and eliminate very costly errors (e.g. type checking).
Verification provides hard assurances under explicit assumptions.
Verification by construction (e.g., transformational programming) provides concrete design methods.
Limits of Verification
Full verification may be extraordinarily more complex than the programs and their specifications.
Manual verification is tedious and error-prone.
Correctness Driven Program Design
Start with (mathematical) specification. Refine/transform step-by-step specification
to efficient program. Verify that each step is correct.
Correctness Driven Program Design: Logical basis
Imperative programming: Floyd-Hoare logic Weakest preconditions (Dijkstra)
Functional programming: Lambda calculus Type theory Fold/unfold transformations (Burstall,
Darlington)