f x = if (x=0) then 1 else x * f(x-1) ack (0,n) = n+1 ack (Suc m,0) = ack (m, 1) ack (Suc m, Suc n) = ack (m, ack (Suc m, n))but that the following are not (the first two have overlapping patterns, and the second function has two arguments):
g (0,x) = 1 g (y,0) = 2 f 0 = 1 f y = y * f (x-1) A 0 n = n+1 A (Suc m) 0 = A m 1 A (Suc m) (Suc n) = A m (A (Suc m) n)
Fortunately, it is usually simple to build the right wellfounded relation. The basic wellfounded relation for a datatype is the immediate subterm relation. This is wellfounded since one can only finitely often take the immediate subterm of a datatype element before getting to a term with no subterms. For example, in the natural numbers, the predecessor relation is the immediate subterm relation. Using the immediate subterm relation for a datatype gives primitive recursive definitions, but we can make much more powerful termination expressions by use of the following combining forms: transitive closure, inverse image, and lexicographic combination (there are others available, but these are the most useful). Following are the definitions:
inv_image R f = \x y. R (f x) (f y)) lexico R1 R2 = \(s,t) (u,v). R1 s u \/ (s = u) /\ R2 t v) measure = inv_image (<)Now we give some example termination expressions and describe what functions would terminate under them.
The structures Rules, Thms, Thry, and USyntax represent the basis on which TFL is built. They provide such things as common syntactic manipulations, specific theorems and inference rules, and access to the underlying theory mechanism. The ordinary user should have no great need to look at the implementations of these structures.
TFL also uses a collection of types:
val mk_functional : Thry -> Preterm -> {functional:Preterm, patterns:Preterm list}mk_functional takes a theory and a conjunction of recursion equations and returns the functional corresponding to the equations and also the patterns used in the equations. The main component in this operation is an ML-style pattern-matching translation. For example, in HOL, with recursion equations
(Fact 0 = 1) /\ (Fact (Suc x) = Fact x * Suc x)the output of mk_functional is
{functional = `\Fact a. num_case 1 (\v. Fact v * Suc v) a`, patterns = [`0`,`Suc x`]}.Note that the function being represented (Fact in this case) should be a variable in the recursion equations.
val prim_wfrec_definition : Thry -> {R:Preterm, functional:Preterm} -> {def:Thm, corollary:Thm, theory:Thry}prim_wfrec_definition is the main underlying definition mechanism. It takes a relation R and a functional which looks like \f x. N, the latter presumably derived from mk_functional, and makes the definition def
f = WFREC R (\f x. N).It then instantiates and simplifies the wellfounded recursion theorem to get corollary. For our example, we get, using (\m n. n = Suc m) for R:
{def= |- Fact = WFREC (\m n. n = Suc m) (\Fact a. num_case 1 (\v. Fact v * Suc v) a), corollary= |- WF (\m n. n = Suc m) ==> !x. Fact x = (\Fact a. num_case 1 (\v. Fact v * Suc v) a) (Fact % (\m n. n = Suc m),x) x}.
val gen_wfrec_definition : Thry -> {R:Preterm, eqs:Preterm} -> {theory:Thry, rules:Thm, TCs:Preterm list list}gen_wfrec_definition acts essentially as a pre- and post-processor for prim_wfrec_definition. It takes R as before, and takes eqs, a conjunction of recursion equations. It then calls mk_functional to translate the recursion equations to a functional, and then invokes prim_wfrec_definition on the result to make the definition. After that, there is a phase of termination condition extraction: this traverses the definition and brings out formulae that, when proven, allow the definition to be used freely. For example, an invocation with R = (\m n. n = Suc m) and eqs = fact x = if (x = 0) then 1 else x * fact(x-1) gives the result (momentarily ignoring the returned theory component):
{rules = [WF (\m n. n = SUC m)] |- (!x. ~(x = 0) ==> (\m n. n = SUC m) (x - 1) x) ==> (fact x = ((x = 0) => 1 | (x * fact (x - 1)))), TCs = [[`!x. ~(x = 0) ==> (\m n. n = SUC m) (x - 1) x`]]}Note well that even if these are not proven, the definition can still be used. This is in contrast to some other approaches, where termination must be proved before anything can be doen with the function.
val wfrec_eqns : Thry -> Preterm -> Preterm * Preterm * (Thm * Preterm list) list val lazyR_def : Thry -> Preterm -> {theory: Thry, eqns:Thm, TCs:Preterm list list}wfrec_eqns is used to implement lazyR_def, and is not of much interest.
val mk_induction : Thry -> Thm -> Preterm list list -> Thm
val postprocess: {WFtac:Tactic, terminator:Tactic, simplifier:Term -> Thm} -> Thry -> {rules:Thm, induction:Thm, TCs:Preterm list list} -> {rules:Thm, induction:Thm, TCs:Thm list}
val new_context : Thm -> unit val context_congs : unit -> Thm list