diff -r 5370cfbf3070 -r 547224bf9348 doc-src/IsarAdvanced/Functions/Thy/Functions.thy --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc-src/IsarAdvanced/Functions/Thy/Functions.thy Tue Nov 07 12:20:11 2006 +0100 @@ -0,0 +1,250 @@ +(* Title: Doc/IsarAdvanced/Functions/Thy/Fundefs.thy + ID: $Id$ + Author: Alexander Krauss, TU Muenchen + +Tutorial for function definitions with the new "function" package. +*) + +theory Functions +imports Main +begin + +chapter {* Defining Recursive Functions in Isabelle/HOL *} +text {* \cite{isa-tutorial} *} + +section {* Function Definition for Dummies *} + +text {* + In most cases, defining a recursive function is just as simple as other definitions: + *} + +fun fib :: "nat \ nat" +where + "fib 0 = 1" +| "fib (Suc 0) = 1" +| "fib (Suc (Suc n)) = fib n + fib (Suc n)" + +(*<*)termination by lexicographic_order(*>*) + +text {* + The function always terminates, since the argument of gets smaller in every + recursive call. Termination is an + important requirement, since it prevents inconsistencies: From + the "definition" @{text "f(n) = f(n) + 1"} we could prove + @{text "0 = 1"} by subtracting @{text "f(n)"} on both sides. + + Isabelle tries to prove termination automatically when a function is + defined. We will later look at cases where this fails and see what to + do then. +*} + +subsection {* Pattern matching *} + +text {* + Like in functional programming, functions can be defined by pattern + matching. At the moment we will only consider \emph{datatype + patterns}, which only consist of datatype constructors and + variables. + + If patterns overlap, the order of the equations is taken into + account. The following function inserts a fixed element between any + two elements of a list: +*} + +fun sep :: "'a \ 'a list \ 'a list" +where + "sep a (x#y#xs) = x # a # sep a (y # xs)" +| "sep a xs = xs" +(*<*)termination by lexicographic_order(*>*) + +text {* + Overlapping patterns are interpreted as "increments" to what is + already there: The second equation is only meant for the cases where + the first one does not match. Consequently, Isabelle replaces it + internally by the remaining cases, making the patterns disjoint. + This results in the equations @{thm [display] sep.simps[no_vars]} +*} + + + + + + + +text {* + The equations from function definitions are automatically used in + simplification: +*} + +lemma "fib (Suc (Suc 0)) = (Suc (Suc 0))" +by simp + + + + +subsection {* Induction *} + +text {* FIXME *} + + +section {* If it does not work *} + +text {* + Up to now, we were using the \cmd{fun} command, which provides a + convenient shorthand notation for simple function definitions. In + this mode, Isabelle tries to solve all the necessary proof obligations + automatically. If a proof does not go through, the definition is + rejected. This can mean that the definition is indeed faulty, like, + or that the default proof procedures are not smart enough (or + rather: not designed) to handle the specific definition. +. + By expanding the abbreviation to the full \cmd{function} command, the + proof obligations become visible and can be analyzed and solved manually. +*} + +(*<*)types "\" = "nat \ nat" +(*>*) +fun f :: "\" +where + (*<*)"f x = x" (*>*)text {* \vspace{-0.8cm}\emph{equations} *} + +text {* \noindent abbreviates *} + +function (sequential) fo :: "\" +where + (*<*)"fo x = x" (*>*)txt {* \vspace{-0.8cm}\emph{equations} *} +by pat_completeness auto +termination by lexicographic_order + +text {* + Some declarations and proofs have now become explicit: + + \begin{enumerate} + \item The "sequential" option enables the preprocessing of + pattern overlaps we already saw. Without this option, the equations + must already be disjoint and complete. The automatic completion only + works with datatype patterns. + + \item A function definition now produces a proof obligation which + expresses completeness and compatibility of patterns (We talk about + this later). The combination of the methods {\tt pat\_completeness} and + {\tt auto} is used to solve this proof obligation. + + \item A termination proof follows the definition, started by the + \cmd{termination} command. The {\tt lexicographic\_order} method can prove termination of a + certain class of functions by searching for a suitable lexicographic combination of size + measures. + \end{enumerate} + *} + + +section {* Proving termination *} + +text {* + Consider the following function, which sums up natural numbers up to + @{text "N"}, using a counter @{text "i"} +*} + +function sum :: "nat \ nat \ nat" +where + "sum i N = (if i > N then 0 else i + sum (Suc i) N)" + by pat_completeness auto + +text {* + The {\tt lexicographic\_order} method fails on this example, because none of the + arguments decreases in the recursive call. + + A more general method for termination proofs is to supply a wellfounded + relation on the argument type, and to show that the argument + decreases in every recursive call. + + The termination argument for @{text "sum"} is based on the fact that + the \emph{difference} between @{text "i"} and @{text "N"} gets + smaller in every step, and that the recursion stops when @{text "i"} + is greater then @{text "n"}. Phrased differently, the expression + @{text "N + 1 - i"} decreases in every recursive call. + + We can use this expression as a measure function to construct a + wellfounded relation, which is a proof of termination: +*} + +termination + by (auto_term "measure (\(i,N). N + 1 - i)") + +text {* + Note that the two (curried) function arguments appear as a pair in + the measure function. The \cmd{function} command packs together curried + arguments into a tuple to simplify its internal operation. Hence, + measure functions and termination relations always work on the + tupled type. + + Let us complicate the function a little, by adding some more recursive calls: +*} + +function foo :: "nat \ nat \ nat" +where + "foo i N = (if i > N + then (if N = 0 then 0 else foo 0 (N - 1)) + else i + foo (Suc i) N)" +by pat_completeness auto + +text {* + When @{text "i"} has reached @{text "N"}, it starts at zero again + and @{text "N"} is decremented. + This corresponds to a nested + loop where one index counts up and the other down. Termination can + be proved using a lexicographic combination of two measures, namely + the value of @{text "N"} and the above difference. The @{text "measures"} + combinator generalizes @{text "measure"} by taking a list of measure functions. +*} + +termination + by (auto_term "measures [\(i, N). N, \(i,N). N + 1 - i]") + + +section {* Mutual Recursion *} + +text {* + If two or more functions call one another mutually, they have to be defined + in one step. The simplest example are probably @{text "even"} and @{text "odd"}: +*} +(*<*)hide const even odd(*>*) + +function even odd :: "nat \ bool" +where + "even 0 = True" +| "odd 0 = False" +| "even (Suc n) = odd n" +| "odd (Suc n) = even n" + by pat_completeness auto + +text {* + To solve the problem of mutual dependencies, Isabelle internally + creates a single function operating on the sum + type. Then the original functions are defined as + projections. Consequently, termination has to be proved + simultaneously for both functions, by specifying a measure on the + sum type: +*} + +termination + by (auto_term "measure (sum_case (%n. n) (%n. n))") + + + +(* FIXME: Mutual induction *) + + + +section {* Nested recursion *} + +text {* FIXME *} + +section {* More general patterns *} + +text {* FIXME *} + + + + +end