Major Section: EVENTS
Examples: (defun app (x y) (if (consp x) (cons (car x) (app (cdr x) y)) y)) (defun fact (n) (declare (xargs :guard (and (integerp n) (>= n 0)))) (if (zp n) 1 (* n (fact (1- n))))) General Form: (defun fn (var1 ... varn) doc-string dcl ... dcl body),where
fn
is the symbol you wish to define and is a new symbolic name
(see name), (var1 ... varn)
is its list of formal parameters
(see name), and body
is its body. The definitional axiom is logically
admissible provided certain restrictions are met. These are sketched below.Note that ACL2 does not support the use of lambda-list
keywords (such as
&optional
) in the formals list of functions. We do support some such
keywords in macros and often you can achieve the desired syntax by defining a
macro in addition to the general version of your function. See defmacro.
The documentation string, doc-string
, is optional; for a
description of its form, see doc-string.
The declarations (see declare), dcl
, are also optional. If more
than one dcl
form appears, they are effectively grouped together as one.
Perhaps the most commonly used ACL2 specific declaration is of the form
(declare (xargs :guard g :measure m))
. This declaration in the defun
of some function fn
has the effect of making the ``guard'' for
fn
be the term g
and the ``measure'' be the term m
. The notion
of ``measure'' is crucial to ACL2's definitional principle. The notion of
``guard'' is not, and is discussed elsewhere; see verify-guards and
see set-verify-guards-eagerness. Note that the :measure
is ignored in
:
program
mode; see defun-mode.
We now briefly discuss the ACL2 definitional principle, using the following definition form which is offered as a more or less generic example.
(defun fn (x y) (declare (xargs :guard (g x y) :measure (m x y))) (if (test x y) (stop x y) (step (fn (d x) y))))Note that in our generic example,
fn
has just two arguments, x
and
y
, the guard and measure terms involve both of them, and the body is
a simple case split on (test x y)
leading to a ``non-recursive'' branch,
(stop x y)
, and a ``recursive'' branch. In the recursive branch, fn
is called after ``decrementing'' x
to (d x)
and some step function is
applied to the result. Of course, this generic example is quite specific in
form but is intended to illustrate the more general case.Provided this definition is admissible under the logic, as outlined below, it adds the following axiom to the logic.
Defining Axiom: (fn x y) = (if (test x y) (stop x y) (step (fn (d x) y)))Note that the guard of
fn
has no bearing on this logical axiom.This defining axiom is actually implemented in the ACL2 system by a
:
definition
rule, namely
(equal (fn x y) (if (test a b) (stop a b) (step (fn (d a) b)))).See definition for a discussion of how definition rules are applied. Roughly speaking, the rule causes certain instances of
(fn x y)
to be replaced by
the corresponding instances of the body above. This is called ``opening up''
(fn x y)
. The instances of (fn x y)
opened are chosen primarily by
heuristics which determine that the recursive calls of fn
in the opened
body (after simplification) are more desirable than the unopened call of
fn
.This discussion has assumed that the definition of fn
was admissible.
Exactly what does that mean? First, fn
must be a previously
unaxiomatized function symbol (however, see ld-redefinition-action).
Second, the formal parameters must be distinct variable names. Third, the
guard, measure, and body should all be terms and should mention no free
variables except the formal parameters. Thus, for example, body may not
contain references to ``global'' or ``special'' variables; ACL2 constants or
additional formals should be used instead.
The final conditions on admissibility concern the termination of the
recursion. Roughly put, all applications of fn
must terminate. In
particular, there must exist a binary relation, rel
, and some unary
predicate mp
such that rel
is well-founded on objects satisfying
mp
, the measure term m
must always produce something satisfying
mp
, and the measure term must decrease according to rel
in each
recursive call, under the hypothesis that all the tests governing the call
are satisfied. By the meaning of well-foundedness, we know there are no
infinitely descending chains of successively rel
-smaller mp
-objects.
Thus, the recursion must terminate.
The only primitive well-founded relation in ACL2 is o<
(see o<), which
is known to be well-founded on the o-p
s (see o-p). For the proof of
well-foundedness, see proof-of-well-foundedness. However it is possible to
add new well-founded relations. For details, see well-founded-relation. We
discuss later how to specify which well-founded relation is selected by
defun
and in the present discussion we assume, without loss of
generality, that it is o<
on the o-p
s.
For example, for our generic definition of fn
above, with measure term
(m x y)
, two theorems must be proved. The first establishes that m
produces an ordinal:
(o-p (m x y)).The second shows that
m
decreases in the (only) recursive call of fn
:
(implies (not (test x y)) (o< (m (d x) y) (m x y))).Observe that in the latter formula we must show that the ``
m
-size'' of
(d x)
and y
is ``smaller than'' the m
-size of x
and y
,
provided the test, (test x y)
, in the body fails, thus leading to the
recursive call (fn (d x) y)
.See o< for a discussion of this notion of ``smaller than.'' It should be
noted that the most commonly used ordinals are the natural numbers and that
on natural numbers, o<
is just the familiar ``less than'' relation
(<
). Thus, it is very common to use a measure m
that returns a
nonnegative integer, for then (o-p (m x y))
becomes a simple conjecture
about the type of m
and the second formula above becomes a conjecture
about the less-than relationship of nonnegative integer arithmetic.
The most commonly used measure function is acl2-count
, which computes a
nonnegative integer size for all ACL2 objects. See acl2-count.
Probably the most common recursive scheme in Lisp programming is when
some formal is supposed to be a list and in the recursive call it is replaced
by its cdr
. For example, (test x y)
might be simply (atom x)
and (d x)
might be (cdr x)
. In that case, (acl2-count x)
is a
suitable measure because the acl2-count
of a cons
is strictly
larger than the acl2-count
s of its car
and cdr
. Thus,
``recursion by car
'' and ``recursion by cdr
'' are trivially
admitted if acl2-count
is used as the measure and the definition
protects every recursive call by a test insuring that the decremented
argument is a consp
. Similarly, ``recursion by 1-
'' in which a
positive integer formal is decremented by one in recursion, is also trivially
admissible. See built-in-clause to extend the class of trivially admissible
recursive schemes.
We now turn to the question of which well-founded relation defun
uses.
It should first be observed that defun
must actually select both a
relation (e.g., o<
) and a domain predicate (e.g., o-p
) on which
that relation is known to be well-founded. But, as noted elsewhere
(see well-founded-relation), every known well-founded relation has a unique
domain predicate associated with it and so it suffices to identify simply the
relation here.
The xargs
field of a declare
permits the explicit specification
of any known well-founded relation with the keyword
:
well-founded-relation
. An example is given below. If the
xargs
for a defun
specifies a well-founded relation, that relation
and its associated domain predicate are used in generating the termination
conditions for the definition.
If no :
well-founded-relation
is specified, defun
uses the
:
well-founded-relation
specified in the acl2-defaults-table
.
See set-well-founded-relation to see how to set the default well-founded
relation (and, implicitly, its domain predicate). The initial default
well-founded relation is o<
(with domain predicate o-p
).
This completes the brief sketch of the ACL2 definitional principle. Optionally, see ruler-extenders for a more detailed discussion of the termination analysis and resulting proof obligations for admissibility, as well as a discussion of the relation to how ACL2 stores induction schemes.
On very rare occasions ACL2 will seem to "hang" when processing a
definition, especially if there are many subexpressions of the body whose
function symbol is if
(or which macroexpand to such an expression). In
those cases you may wish to supply the following to xargs
:
:normalize nil
. This is an advanced feature that turns off ACL2's usual
propagation upward of if
tests.
The following example illustrates all of the available declarations and most hint keywords, but is completely nonsensical. For documentation, see xargs and see hints.
(defun example (x y z a b c i j) (declare (ignore a b c) (type integer i j) (xargs :guard (symbolp x) :measure (- i j) :ruler-extenders :basic :well-founded-relation my-wfr :hints (("Goal" :do-not-induct t :do-not '(generalize fertilize) :expand ((assoc x a) (member y z)) :restrict ((<-trans ((x x) (y (foo x))))) :hands-off (length binary-append) :in-theory (set-difference-theories (current-theory :here) '(assoc)) :induct (and (nth n a) (nth n b)) :use ((:instance assoc-of-append (x a) (y b) (z c)) (:functional-instance (:instance p-f (x a) (y b)) (p consp) (f assoc))))) :guard-hints (("Subgoal *1/3'" :use ((:instance assoc-of-append (x a) (y b) (z c))))) :mode :logic :normalize nil :verify-guards nil :non-executable t :otf-flg t)) (example-body x y z i j))