Reasoning about programs. Chapter 9 of Thompson

Similar documents
Week 5 Tutorial Structural Induction

PROGRAMMING IN HASKELL. CS Chapter 6 - Recursive Functions

Isabelle s meta-logic. p.1

FUNCTIONAL PEARLS The countdown problem

Theorem Proving Principles, Techniques, Applications Recursion

Introduction to the Lambda Calculus

Shell CSCE 314 TAMU. Functions continued

Foundations of Computation

Introduction. chapter Functions

Programming Languages 3. Definition and Proof by Induction

introduction to Programming in C Department of Computer Science and Engineering Lecture No. #40 Recursion Linear Recursion

(a) Give inductive definitions of the relations M N and M N of single-step and many-step β-reduction between λ-terms M and N. (You may assume the

Fall 2018 Lecture N

Verifying Safety Property of Lustre Programs: Temporal Induction

Basic Foundations of Isabelle/HOL

Program Calculus Calculational Programming

(a) (4 pts) Prove that if a and b are rational, then ab is rational. Since a and b are rational they can be written as the ratio of integers a 1

Shell CSCE 314 TAMU. Higher Order Functions

STABILITY AND PARADOX IN ALGORITHMIC LOGIC

Lecture 10 September 11, 2017

Testing. Wouter Swierstra and Alejandro Serrano. Advanced functional programming - Lecture 2. [Faculty of Science Information and Computing Sciences]

Abstract Interpretation Using Laziness: Proving Conway s Lost Cosmological Theorem

Programming Languages Third Edition

The List Datatype. CSc 372. Comparative Programming Languages. 6 : Haskell Lists. Department of Computer Science University of Arizona

Lecture 7: Primitive Recursion is Turing Computable. Michael Beeson

Overview. A Compact Introduction to Isabelle/HOL. Tobias Nipkow. System Architecture. Overview of Isabelle/HOL

1.3. Conditional expressions To express case distinctions like

Lesson 20: Every Line is a Graph of a Linear Equation

COMP4161: Advanced Topics in Software Verification. fun. Gerwin Klein, June Andronick, Ramana Kumar S2/2016. data61.csiro.au

Introduction to Automata Theory. BİL405 - Automata Theory and Formal Languages 1

CSE 20 DISCRETE MATH WINTER

Programming Languages Fall 2013

Recursive Data and Recursive Functions

Functional Programming. Overview. Topics. Definition n-th Fibonacci Number. Graph

Shell CSCE 314 TAMU. Haskell Functions

Chapter 3. Set Theory. 3.1 What is a Set?

ELEMENTARY NUMBER THEORY AND METHODS OF PROOF

Recursion. Tjark Weber. Functional Programming 1. Based on notes by Sven-Olof Nyström. Tjark Weber (UU) Recursion 1 / 37

CSCE 314 TAMU Fall CSCE 314: Programming Languages Dr. Flemming Andersen. Haskell Functions

Bases of topologies. 1 Motivation

Type Processing by Constraint Reasoning

CSci 450: Org. of Programming Languages Overloading and Type Classes

Side Effects (3A) Young Won Lim 1/13/18

Induction and Recursion. CMPS/MATH 2170: Discrete Mathematics

Denotational semantics

Induction and Semantics in Dafny

CSCE 314 Programming Languages

This session. Recursion. Planning the development. Software development. COM1022 Functional Programming and Reasoning

Diagonalization. The cardinality of a finite set is easy to grasp: {1,3,4} = 3. But what about infinite sets?

QuickCheck, SmallCheck & Reach: Automated Testing in Haskell. Tom Shackell

Induction for Data Types

Lecture 19: Functions, Types and Data Structures in Haskell

Lecture 5: Lazy Evaluation and Infinite Data Structures

CS103 Spring 2018 Mathematical Vocabulary

Software Properties as Axioms and Theorems

Fall Recursion and induction. Stephen Brookes. Lecture 4

1. Chapter 1, # 1: Prove that for all sets A, B, C, the formula

CS 44 Exam #2 February 14, 2001

1KOd17RMoURxjn2 CSE 20 DISCRETE MATH Fall

To illustrate what is intended the following are three write ups by students. Diagonalization

UNIVERSITY OF EDINBURGH COLLEGE OF SCIENCE AND ENGINEERING SCHOOL OF INFORMATICS INFR08013 INFORMATICS 1 - FUNCTIONAL PROGRAMMING

CS 3512, Spring Instructor: Doug Dunham. Textbook: James L. Hein, Discrete Structures, Logic, and Computability, 3rd Ed. Jones and Barlett, 2010

Source-Based Trace Exploration Work in Progress

Introduction to Programming, Aug-Dec 2006

Solutions to In-Class Problems Week 4, Fri

Liquidate your assets

CS 457/557: Functional Languages

3.7 Denotational Semantics

CMSC 330: Organization of Programming Languages. Operational Semantics

Enumerations and Turing Machines

Chapter 1 Programming: A General Overview

Turing Machines. A transducer is a finite state machine (FST) whose output is a string and not just accept or reject.

Combining Static and Dynamic Contract Checking for Curry

This assignment has four parts. You should write your solution to each part in a separate file:

Informatics 1 Functional Programming Lecture 4. Lists and Recursion. Don Sannella University of Edinburgh

Heron Quadrilaterals with Sides in Arithmetic or Geometric Progression

Course year Typeclasses and their instances

p x i 1 i n x, y, z = 2 x 3 y 5 z

Haske k ll An introduction to Functional functional programming using Haskell Purely Lazy Example: QuickSort in Java Example: QuickSort in Haskell

Recursion. What is Recursion? Simple Example. Repeatedly Reduce the Problem Into Smaller Problems to Solve the Big Problem

Types and Static Type Checking (Introducing Micro-Haskell)

An introduction introduction to functional functional programming programming using usin Haskell

Trees. Solution: type TreeF a t = BinF t a t LeafF 1 point for the right kind; 1 point per constructor.

Announcements. CS243: Discrete Structures. Strong Induction and Recursively Defined Structures. Review. Example (review) Example (review), cont.

The Worker/Wrapper Transformation

Types and Static Type Checking (Introducing Micro-Haskell)

Logic and Computation Lecture 20 CSU 290 Spring 2009 (Pucella) Thursday, Mar 12, 2009

Functional Programming with Isabelle/HOL

UNIVERSITY OF EDINBURGH COLLEGE OF SCIENCE AND ENGINEERING SCHOOL OF INFORMATICS INFR08013 INFORMATICS 1 - FUNCTIONAL PROGRAMMING

Math 485, Graph Theory: Homework #3

Semantics of programming languages

Homework 1: Functional Programming, Haskell

1 The language χ. Models of Computation

It is important that you show your work. There are 134 points available on this test.

CS 161 Computer Security

Structural polymorphism in Generic Haskell

Chapter 1 Programming: A General Overview

Haskell Introduction Lists Other Structures Data Structures. Haskell Introduction. Mark Snyder

6.001 Notes: Section 15.1

1 Introduction. 3 Syntax

Transcription:

Reasoning about programs Chapter 9 of Thompson

Proof versus testing A proof will state some property of a program that holds for all inputs. Testing shows only that a property holds for a particular set of inputs. Property checking, such as with QuickCheck, improves coverage by randomly generating inputs, but is still not a proof. Proofs in functional programming rely on treating function definitions as logical terms, amenable to manipulation via the rules of logic.

Understanding definitions Consider: length [] = 0 -- (length.1) length (z:zs) = 1 + length zs -- (length.2)

Understanding definitions: by evaluation length [2,3,1] 1 + length [3,1] by (length.2) 1 + (1 + length [1]) by (length.2) 1 + (1 + (1 + length [])) by (length.2) 1 + (1 + (1 + 0)) by (length.1) 3

Understanding definitions: as descriptions (length.1) says what length [] is (length.1) says that whatever values of x and xs we choose, length (x:xs) will be equal to 1 + length xs The second case is a general property of length: how it behaves on all non-empty lists. These allow us to conclude that length [x] = 1 (length.3)... but how?

Understanding definitions: as descriptions length [x] = length (x:[]) by definition of [x] = 1 + length [] by (length.2) = 1 + 0 by (length.1) = 1 We can read a definition as: 1. describing how to compute particular results, and 2. a general description of the behaviour of the function, allowing deductions to be made like (length.3) and others like length (xs ++ ys) = length xs + length ys

Proof as symbolic evaluation Instead of using a particular value as argument to length, like 2, we replace 2 with a symbolic variable x, but use the evaluation rules in the same way. Combining this symbolic evaluation with other proof techniques (like induction) allows many proofs for recursive functions.

Testing mysterymax :: Integer -> Integer -> Integer -> Integer mysterymax x y z x > y && x > z = x y > x && y > z = y otherwise = z prop_mystery :: Integer -> Integer -> Integer -> Bool prop_mystery x y z = mysterymax x y z == (x `max` y) `max` z

Proof by cases Consider: x > y && x > z y > x && y > z z > x && z > y For each of these cases, mysterymax is correct.

Proof by cases For all other cases at least two of the arguments are equal. If all three are equal: x == y && y == z then mysterymax is correct. Suppose: y == z && z > x then it is still correct. But, for: x == y && y > z theresult z is incorrect!

Proof by cases A form of symbolic testing case by case, for classes of inputs. However, in general, finding a proof of correctness is more difficult than this example. So, concrete testing is still a valuable exercise.

Definedness and termination Evaluation can have two outcomes: the evaluation can halt (terminate) with an answer the evaluation can go on forever (the value is undefined) Consider: fact :: Integer -> Integer fact n n==0 = 1 otherwise = n * fact (n-1)

Definedness and termination fact 2 terminates. fact (-2) is undefined: fact (-2) (-2) * fact (-3) (-2) * (-3) * fact (-4)...

Definedness and termination Proofs must confine themselves to cases for defined values where expected properties hold. 0 * e = 0, but only if e is defined 0 * e = undefined 0, if e is undefined Proofs usually hold only for all defined values. Undefined values are only of interest if the function in question does not give a defined value when it is expected to.

Finiteness Haskell evaluation is lazy, so arguments are evaluated only if their values are actually needed. Lazy evaluation allows definition and use of infinite lists, like [1,2,3,...] and partially defined lists. Our main attention will be to finite lists, which have a defined, finite length, and defined elements, e.g.: [] [1,2,3] [[4,5],[3,2,1],[]]

Assumptions in proofs Logical implication A B says that if A holds then B also holds (B follows from A) Proving an implication A B, we can assume A in proving B, and then simply need to prove A to guarantee our proof of B. A proof of A B is a process for turning a proof of A into a proof of B. In proof by induction, the induction step proves one property assuming another.

Free variables and quantifiers Equational reasoning implicitly quantifies over all possible values of free variables: square x = x * x says this holds for all (defined) values of the free variable x. More explicitly, we should actually write this with a logical quantifier: x (square x = x * x)

Induction Consider: sum :: [Integer] -> Integer sum [] = 0 -- (sum.1) sum (x:xs) = x + sum xs -- (sum.2) This gives a value outright at [], and defines the value of sum (x:xs) using the value sum xs

Principle of structural induction for lists In order to prove that a logical property P(xs) holds for all finite lists xs we have to do two things: Base case: Prove P([]) outright. Induction step: Prove P(x:xs) on the assumption that P(xs) holds. In other words P(xs) P(x:xs) has to be proved. The P(xs) is called the induction hypothesis since it is assumed in proving P(x:xs). This is just like primitive recursion: instead of building values of a function we build up parts of a proof. In both cases [] is a base case, and the general case goes from xs to (x:xs).

Justification of structural induction for lists Just as recursion is not circular, proof by induction builds a proof for all finite lists in stages. Given proofs of P([]) and P(xs) P(x:xs) for all x and xs suppose we want to show that P([1,2,3]): 1. P([]) holds; 2. P([]) P([3]) holds, since it is a case of P(xs) P(x:xs); 3. 1 & 2 give us that P([3]) holds; 4. P([3]) P([2,3]) holds, as for 2; 5. 3 & 4 give us that P([2,3]) holds; 6. P([2,3]) P([1,2,3]) holds, as for 2; 7. 5 & 6 give us that P([1,2,3]) holds. This works for all finite lists, so we have P(xs) for all finite lists.

Example: doubleall Consider: doubleall :: [Integer] -> [Integer] doubleall [] = [] -- (doubleall.1) doubleall (z:zs) = 2*z : doubleall zs -- (doubleall.2) Presumably: sum (doubleall xs) = 2 * sum xs -- (sum+dblall) [quickcheck property testing passes]

Example: doubleall Two induction goals: sum (doubleall []) = 2 * sum [] sum (doubleall (x:xs)) = 2 * sum (x:xs) using the induction hypothesis: sum (doubleall xs) = 2 * sum xs -- (base) -- (ind) -- (hyp)

Example: doubleall The base case: sum (doubleall []) = sum ([]) by (doubleall.1) = 0 by (sum.1) 2 * sum ([]) = 2 * 0 by (sum.1) = 0 by *

Example: doubleall The induction step: Now: sum (doubleall (x:xs)) = sum (2*x : doubleall xs) by (doubleall.2) = 2*x + sum (doubleall xs) by (sum.2) 2 * sum (x:xs) = 2 * (x + sum xs) by (sum.2) = 2*x + 2 * sum xs by distribution of * 2*x + sum (doubleall xs) = 2*x + 2 * sum xs by (hyp) QED

Finding induction proofs First step: define a QuickCheck property, and ensure it generates no counter examples. State the goal of the induction and the two sub-goals: (base) and (hyp) (ind) Change variable names as needs to avoid confusion (α-conversion) Use only definitions of functions involved and general rules of arithmetic to simplify sub-goals (for equations do LHS and RHS separately) For the induction step (ind) use (hyp) in its proof Label each step of the proof with its justification