Com S 541 Lecture -*- Outline -*- * reasoning in functional languages contrasted with imperative languages (omit) reasoning with side effects, aliases, etc. (Davie 1.2) Q: How do you specify and verify imperative programs? One way: specification for each block of code is - predicate describing the state at end (postcondition) - predicate describing what doesn't change (frame) - predicate describing requirements on initial state (postcondition) this forms a contract, which must be met by implementation may need auxiliary math definitions to help write predicates ------------------------------------------ SPECIFICATION AND VERIFICATION FOR IMPERATIVE PROGRAMS // Larch/C++ behavioral specification //@ uses FactorialTrait; void factAssign(int & x, int n); //@ behavior { //@ requires n >= 0; //@ modifies x; //@ ensures liberally x' = factorial(n); //@ } % Vocabulary definition in LSL FactorialTrait: trait assumes int introduces factorial: int -> int asserts \forall i: int factorial(0) == 1; (i >= 0) => (factorial(i+1) = (i+1) * factorial(i)); ------------------------------------------ LSL is Larch Shared Language (for this, it's equational algebraic) ------------------------------------------ // CODE in C++ with VERIFICATION void factAssign(int & x, int n) { // ASSERT: n >= 0 int i = 0; x = 1; // INV: n >= 0 /\ x = factorial(i) // /\ i <= n while (i < n) { i++; // ASSERT: n >= 0 // /\ x = factorial(i-1) // /\ i-1 <= n x *= i; } // ASSERT: x = factorial(i) /\ i = n // hence // ASSERT: x = factorial(n) } ------------------------------------------ This is an example of replacing constants with fresh variables (Cohen, Programming in the 1990s, Springer, ch. 10). Start reading from the bottom, guess it will take a loop; for loops need to "delete a conjunct" to obtain the invariant, but don't have one, so introduce it, hence the variable i Q: Can the i++ come after the x *= i? Is it in the right order? why? Q: Is the test in the while loop exactly right? For specifying a function, specify it's domain and behavior ------------------------------------------ SPECIFICATION AND VERIFICATION FOR FUNCTIONAL PROGRAMMING -- "Larch/Haskell" behavioral spec. uses FactorialTrait(Integer for int) -- the trait is the same as before fact :: Integer -> Integer fact(j) | j >= 0 = factorial(j) -- implementation in Haskell fact(n) = factIter(n,1) fact :: (Integer,Integer) -> Integer factIter(0, i) = i factIter(n+1, i) = factIter(n, (n+1)*i) ------------------------------------------ specifies interface, precondition (domain). nothing changes, so no frame ------------------------------------------ VERIFICATION USING EQUATIONAL REASONING Theorem: if j > 0, fact(j) = factorial(j) Proof: fact(j) = factIter(j,1) = factorial(j)*1 Lemma: For all i >= 0, factIter(i,k) = factorial(i)*k Proof: Basis: j = 0 factIter(0,k) = { definition } k = { arithmetic } 1 * k = { specification } factorial(0) * k Inductive step: j = n+1 Ind Hyp: factIter(n,k) = factorial(n)*k factIter(n+1, k) = { definition } factIter(n, (n+1)*k) = { Ind Hyp} factorial(n)*(n+1)*k = { definition of factorial } factorial(n+1)*k ------------------------------------------ Obviously, you don't have to be so careful in either case Q: How would you compare the two proofs? Q: Which uses simpler math? Which would be easier to teach? Q: Which would you be more confident in? ------------------------------------------ VERIFICATION PROBLEMS WITH IMPERATIVE LANGUAGES Aliasing: two names for the same variable Is this valid for C++? // ASSERT: x = 4 /\ y = 4 x = 7; // ASSERT: x = 7 /\ y = 4 Not referentially transparent: an expression changes meaning over time Is this valid for C++? b = (f() == f()) // ASSERT: b = true Others: - ------------------------------------------ Q: What other problems (bugs) are typical in imperative languages? dangling refererences, unitialized variables... other reasoning problems include: implicit arguments (store)