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