Major Section: RULE-CLASSES
See rule-classes for a general discussion of rule classes and how they are
used to build rules from formulas. An example :
corollary
formula
from which a :definition
rule might be built is:
Example: (implies (true-listp x) (equal (len x) (if (null x) 0 (if (null (cdr x)) 1 (+ 2 (len (cddr x))))))) General Form: (implies hyp (equiv (fn a1 ... an) body))where
equiv
is an equivalence relation and fn
is a function symbol
other than if
, hide
, force
or case-split
. Such rules
allow ``alternative'' definitions of fn
to be proved as theorems but used
as definitions. These rules are not true ``definitions'' in the sense that
they (a) cannot introduce new function symbols and (b) do not have to be
terminating recursion schemes. They are just conditional rewrite rules that
are controlled the same way we control recursive definitions. We call these
``definition rules'' or ``generalized definitions''.Consider the general form above. Generalized definitions are stored among
the :
rewrite
rules for the function ``defined,'' fn
above, but
the procedure for applying them is a little different. During rewriting,
instances of (fn a1 ... an)
are replaced by corresponding instances of
body
provided the hyp
s can be established as for a :
rewrite
rule and the result of rewriting body
satisfies the criteria for function
expansion. There are two primary criteria, either of which permits
expansion. The first is that the ``recursive'' calls of fn
in the
rewritten body have arguments that already occur in the goal conjecture. The
second is that the ``controlling'' arguments to fn
are simpler in the
rewritten body.
The notions of ``recursive call'' and ``controllers'' are complicated by the provisions for mutually recursive definitions. Consider a ``clique'' of mutually recursive definitions. Then a ``recursive call'' is a call to any function defined in the clique and an argument is a ``controller'' if it is involved in the measure that decreases in all recursive calls. These notions are precisely defined by the definitional principle and do not necessarily make sense in the context of generalized definitional equations as implemented here.
But because the heuristics governing the use of generalized definitions
require these notions, it is generally up to the user to specify which calls
in body are to be considered recursive and what the controlling arguments
are. This information is specified in the :clique
and
:controller-alist
fields of the :definition
rule class.
The :clique
field is the list of function symbols to be considered
recursive calls of fn
. In the case of a non-recursive definition, the
:clique
field is empty; in a singly recursive definition, it should
consist of the singleton list containing fn
; otherwise it should be a
list of all of the functions in the mutually recursive clique with this
definition of fn
.
If the :clique
field is not provided it defaults to nil
if fn
does not occur as a function symbol in body
and it defaults to the
singleton list containing fn
otherwise. Thus, :clique
must be
supplied by the user only when the generalized definition rule is to be
treated as one of several in a mutually recursive clique.
The :controller-alist
is an alist that maps each function symbol in the
:clique
to a mask specifying which arguments are considered controllers.
The mask for a given member of the clique, fn
, must be a list of t
's
and nil
's of length equal to the arity of fn
. A t
should be in
each argument position that is considered a ``controller'' of the recursion.
For a function admitted under the principle of definition, an argument
controls the recursion if it is one of the arguments measured in the
termination argument for the function. But in generalized definition rules,
the user is free to designate any subset of the arguments as controllers.
Failure to choose wisely may result in the ``infinite expansion'' of
definitional rules but cannot render ACL2 unsound since the rule being
misused is a theorem.
If the :controller-alist
is omitted it can sometimes be defaulted
automatically by the system. If the :clique
is nil
, the
:controller-alist
defaults to nil
. If the :clique
is a singleton
containing fn
, the :controller-alist
defaults to the controller alist
computed by (defun fn args body)
. (The user can obtain some control over
this analysis by setting the default ruler-extenders;
see ruler-extenders.) If the :clique
contains more than one function,
the user must supply the :controller-alist
specifying the controllers for
each function in the clique. This is necessary since the system cannot
determine and thus cannot analyze the other definitional equations to be
included in the clique.
For example, suppose fn1
and fn2
have been defined one way and it is
desired to make ``alternative'' mutually recursive definitions available to
the rewriter. Then one would prove two theorems and store each as a
:definition
rule. These two theorems would exhibit equations
``defining'' fn1
and fn2
in terms of each other. No provision is
here made for exhibiting these two equations as a system of equations. One
is proved and then the other. It just so happens that the user intends them
to be treated as mutually recursive definitions. To achieve this end, both
:definition
rules should specify the :clique
(fn1 fn2)
and should
specify a suitable :controller-alist
. If, for example, the new
definition of fn1
is controlled by its first argument and the new
definition of fn2
is controlled by its second and third (and they each
take three arguments) then a suitable :controller-alist
would be
((fn1 t nil nil) (fn2 nil t t))
. The order of the pairs in the alist is
unimportant, but there must be a pair for each function in the clique.
Inappropriate heuristic advice via :clique
and :controller-alist
can
cause ``infinite expansion'' of generalized definitions, but cannot render
ACL2 unsound.
Note that the actual definition of fn1
has the runic name
(:definition fn1)
. The runic name of the alternative definition is
(:definition lemma)
, where lemma
is the name given to the event that
created the generalized :definition
rule. This allows theories to switch
between various ``definitions'' of the functions.
By default, a :definition
rule establishes the so-called ``body'' of a
function. The body is used by :expand
hints, and it is also used
heuristically by the theorem prover's preprocessing (the initial
simplification using ``simple'' rules that is controlled by the
preprocess
symbol in :do-not
hints), induction analysis, and the
determination for when to warn about non-recursive functions in rules. The
body is also used by some heuristics involving whether a function is
recursively defined, and by the expand
, x
, and x-dumb
commands of
the proof-checker.
See rule-classes for a discussion of the optional field :install-body
of
:definition
rules, which controls whether a :definition
rule is used
as described in the paragraph above. Note that even if :install-body nil
is supplied, the rewriter will still rewrite with the :definition
rule;
in that case, ACL2 just won't install a new body for the top function symbol
of the left-hand side of the rule, which for example affects the application
of :expand
hints as described in the preceding paragraph. Also
see set-body and see show-bodies for how to change the body of a function
symbol.
Note only that if you prove a definition rule for function foo
, say,
foo-new-def
, you will need to refer to that definition as foo-new-def
or as (:DEFINITION foo-new-def)
. That is because a :definition
rule
does not change the meaning of the symbol foo
for :use
hints,
nor does it change the meaning of the symbol foo
in theory expressions;
see theories, in particular the discussion there of runic designators.
Similarly :
pe
foo
and :
pf
foo
will still show the
original definition of foo
.
The definitional principle, defun
, actually adds :definition
rules.
Thus the handling of generalized definitions is exactly the same as for
``real'' definitions because no distinction is made in the implementation.
Suppose (fn x y)
is defun
'd to be body
. Note that defun
(or defuns
or mutual-recursion
) can compute the clique for fn
from the syntactic presentation and it can compute the controllers from the
termination analysis. Provided the definition is admissible, defun
adds the :definition
rule (equal (fn x y) body)
.