DEFEXEC.html -- ACL2 Version 6.3

## DEFEXEC

attach a terminating executable function to a definition

    Major Section:  EVENTS

Suppose you define a function `(fn x)` with a guard of `(good-input-p x)`, and you know that when the guard holds, the measure decreases on each recursive call. Unfortunately, the definitional principle (see defun) ignores the guard. For example, if the definition has the form

    (defun fn (x)
      (declare (xargs :guard (good-input-p x)))
      (if (not-done-yet x)
          (... (fn (destr x)) ...)
        ...))

then in order to admit this definition, ACL2 must prove the appropriate formula asserting that `(destr x)` is ``smaller than'' `x` under the assumption `(not-done-yet x)` but without the assumption `(good-input-p x)`, even if `(not-done-yet x)` is true. In essence, it may be necessary to submit instead the following definition.

    (defun fn (x)
      (declare (xargs :guard (good-input-p x)))
      (if (good-input-p x)
          (if (not-done-yet x)
              (... (fn (destr x)) ...)
            ...)
        nil)

But it is unfortunate that when calls of `fn` are evaluated, for example when `fn` is applied to an explicit constant during a proof, then a call of `good-input-p` must now be evaluated on each recursive call.

Fortunately, `defexec` provides a way to keep the execution efficient. For the example above we could use the following form.

    (defexec fn (x)
      (declare (xargs :guard (good-input-p x)))
      (mbe :logic (if (good-input-p x)
                      (if (not-done-yet x)
                          (... (fn (destr x)) ...)
                        ...)
                    nil)
           :exec  (if (not-done-yet x)
                      (... (fn (destr x)) ...)
                    ...)))

Here ```mbe`'' stands for ``must be equal'' and, roughly speaking, its call above is logically equal to the `:logic` form but is evaluated using the `:exec` form when the guard holds. See mbe. The effect is thus to define `fn` as shown in the `defun` form above, but to cause execution of `fn` using the `:exec` body. The use of `defexec` instead of `defun` in the example above causes a termination proof to be performed, in order to guarantee that evaluation always theoretically terminates, even when using the `:exec` form for evaluation.

    Example:

    ; Some of the keyword arguments in the declarations below are irrelevant or
    ; unnecessary, but they serve to illustrate their use.

    (defexec f (x)
      (declare (xargs :measure (+ 15 (acl2-count x))
                      :ruler-extenders :basic
                      :hints (("Goal" :in-theory (disable nth)))
                      :guard-hints (("Goal" :in-theory (disable last)))
                      :guard (and (integerp x) (<= 0 x) (< x 25)))
               (exec-xargs
                      :test (and (integerp x) (<= 0 x))
                      :default-value 'undef ; defaults to nil
                      :measure (nfix x)
                      :ruler-extenders :basic
                      :well-founded-relation o<))
      (mbe :logic (if (zp x)
                      1
                    (* x (f (- x 1))))
           :exec  (if (= x 0)
                      1
                    (* x (f (- x 1))))))

The above example macroexpands to the following.

    (ENCAPSULATE ()
     (LOCAL
      (ENCAPSULATE ()
       (SET-IGNORE-OK T)
       (SET-IRRELEVANT-FORMALS-OK T)
       (LOCAL (DEFUN F (X)
                (DECLARE
                 (XARGS :VERIFY-GUARDS NIL
                        :HINTS (("Goal" :IN-THEORY (DISABLE NTH)))
                        :MEASURE (NFIX X)
                        :RULER-EXTENDERS :BASIC
                        :WELL-FOUNDED-RELATION O<))
                (IF (AND (INTEGERP X) (<= 0 X))
                    (IF (= X 0) 1 (* X (F (- X 1))))
                    'UNDEF)))
       (LOCAL (DEFTHM F-GUARD-IMPLIES-TEST
                (IMPLIES (AND (INTEGERP X) (<= 0 X) (< X 25))
                         (AND (INTEGERP X) (<= 0 X)))
                :RULE-CLASSES NIL))))
     (DEFUN F (X)
       (DECLARE (XARGS :MEASURE (+ 15 (ACL2-COUNT X))
                       :RULER-EXTENDERS :BASIC
                       :HINTS (("Goal" :IN-THEORY (DISABLE NTH)))
                       :GUARD-HINTS (("Goal" :IN-THEORY (DISABLE LAST)))
                       :GUARD (AND (INTEGERP X) (<= 0 X) (< X 25))))
       (MBE :LOGIC
            (IF (ZP X) 1 (* X (F (- X 1))))
            :EXEC
            (IF (= X 0) 1 (* X (F (- X 1)))))))

Notice that in the example above, the `:``hints` in the `local` definition of `F` are inherited from the `:hints` in the `xargs` of the `defexec` form. We discuss such inheritance below.

CAVEAT: Termination is not considered for calls of `mbe` under the top-level call. Moreover, the `:exec` part of an `mbe` call under the `:logic` part of any superior `mbe` call is completely ignored.

    General Form:
    (defexec fn (var1 ... varn) doc-string dcl ... dcl
      (mbe :LOGIC logic-body
           :EXEC  exec-body))

where the syntax is identical to the syntax of `defun` where the body is a call of `mbe`, with the exceptions described below. Thus, `fn` is the symbol you wish to define and is a new symbolic name and `(var1 ... varn)` is its list of formal parameters (see name). The first exception is that at least one `dcl` (i.e., `declare` form) must specify a `:guard`, `guard`. The second exception is that one of the `dcl`s is allowed to contain an element of the form `(exec-xargs ...)`. The `exec-xargs` form, if present, must specify a non-empty `keyword-value-listp` each of whose keys is one of `:test`, `:default-value`, or one of the standard `xargs` keys of `:measure`, `:ruler-extenders`, `:well-founded-relation`, `:hints`, or `:stobjs`. Any of these five standard `xargs` keys that is present in an `xargs` of some `dcl` but is not specified in the (possibly nonexistent) `exec-xargs` form is considered to be specified in the `exec-xargs` form, as illustrated in the example above for `:hints`. (So for example, if you want `:hints` in the final, non-local definition but not in the local definition, then specify the `:hints` in the `xargs` but specify `:hints nil` in the `exec-xargs`.) If `:test` is specified and not `nil`, let `test` be its value; otherwise let `test` default to `guard`. If `:default-value` is specified, let `default-value` be its value; else `default-value` is `nil`. `Default-value` should have the same signature as `exec-body`; otherwise the `defexec` form will fail to be admitted.

The above General Form's macroexpansion is of the form `(PROGN encap final-def)`, where `encap` and `final-def` are as follows. `Final-def` is simply the result of removing the `exec-xargs` declaration (if any) from its `declare` form, and is the result of evaluating the given `defexec` form, since `encap` is of the following form.

    ; encap
    (ENCAPSULATE ()
      (set-ignore-ok t)             ; harmless for proving termination
      (set-irrelevant-formals-ok t) ; harmless for proving termination
      (local local-def)
      (local local-thm))

The purpose of `encap` is to ensure the the executable version of `name` terminates on all arguments. Thus, `local-def` and `local-thm` are as follows, where the `xargs` of the `declare` form are the result of adding `:VERIFY-GUARDS NIL` to the result of removing the `:test` and (optional) `:default-value` from the `exec-xargs`.

    ; local-def
    (DEFUN fn formals
      (DECLARE (XARGS :VERIFY-GUARDS NIL ...))
      (IF test
          exec-body
        default-value))

    ; local-thm
    (DEFTHM fn-EXEC-GUARD-HOLDS
      (IMPLIES guard test)
      :RULE-CLASSES NIL)

We claim that if the above `local-def` and `local-thm` are admitted, then all evaluations of calls of `fn` terminate. The concern is that the use of `mbe` in `final-def` allows for the use of `exec-body` for a call of `fn`, as well as for subsequent recursive calls, when `guard` holds and assuming that the guards have been verified for `final-def`. However, by `local-thm` we can conclude in this case that `test` holds, in which case the call of `fn` may be viewed as a call of the version of `fn` defined in `local-def`. Moreover, since guards have been verified for `final-def`, then guards hold for subsequent evaluation of `exec-body`, and in particular for recursive calls of `fn`, which can thus continue to be viewed as calls using `local=def`.