ENCAPSULATE

constrain some functions and/or hide some events
Major Section:  EVENTS

The following example illustrates the idea of encapsulate.

(encapsulate

; Introduce a function foo of one argument that returns one value: ( ((foo *) => *) )

; Introduce a ``witness'' (example) for foo, marked as local so that ; it is not exported: (local (defun foo (x) x))

; Introduce a non-local property to be exported: (defthm foo-preserves-consp (implies (consp x) (consp (foo x)))) )

The form above introduces a new function symbol, foo, with the indicated property and no definition. In fact, the output from ACL2 concludes as follows.

The following constraint is associated with the function FOO:

(IMPLIES (CONSP X) (CONSP (FOO X)))

We turn now to more complete documentation.

Other Examples:
(encapsulate (((an-element *) => *))

; The list of signatures above could also be written ; ((an-element (lst) t))

(local (defun an-element (lst) (if (consp lst) (car lst) nil))) (local (defthm member-equal-car (implies (and lst (true-listp lst)) (member-equal (car lst) lst)))) (defthm thm1 (implies (null lst) (null (an-element lst)))) (defthm thm2 (implies (and (true-listp lst) (not (null lst))) (member-equal (an-element lst) lst))))

(encapsulate () ; empty signature: no constrained functions indicated

(local (defthm hack (implies (and (syntaxp (quotep x)) (syntaxp (quotep y))) (equal (+ x y z) (+ (+ x y) z)))))

(defthm nthcdr-add1-conditional (implies (not (zp (1+ n))) (equal (nthcdr (1+ n) x) (nthcdr n (cdr x))))))

General Form: (encapsulate (signature ... signature) ev1 ... evn)

where each signature is a well-formed signature (see signature), each signature describes a different function symbol, and each evi is an embedded event form (See embedded-event-form). There must be at least one evi. The evi inside local special forms are called ``local'' events below. Events that are not local are sometimes said to be ``exported'' by the encapsulation. We make the further restriction that no defaxiom event may be introduced in the scope of an encapsulate (not even by encapsulate or include-book events that are among the evi). Furthermore, no non-local include-book event is permitted in the scope of any encapsulate with a non-empty list of signatures.

To be well-formed, an encapsulate event must have the properties that each event in the body (including the local ones) can be successfully executed in sequence and that in the resulting theory, each function mentioned among the signatures was introduced via a local event and has the signature listed. (A utility is provided to assist in debugging failures of such execution; see redo-flat.) In addition, the body may contain no ``local incompatibilities'' which, roughly stated, means that the events that are not local must not syntactically require symbols defined by local events, except for the functions listed in the signatures. See local-incompatibility. Finally, no non-local recursive definition in the body may involve in its suggested induction scheme any function symbol listed among the signatures. See subversive-recursions.

The result of an encapsulate event is an extension of the logic in which, roughly speaking, the functions listed in the signatures are constrained to have the signatures listed and to satisfy the non-local theorems proved about them. In fact, other functions introduced in the encapsulate event may be considered to have ``constraints'' as well. (See constraint for details, which are only relevant to functional instantiation.) Since the constraints were all theorems in the ``ephemeral'' or ``local'' theory, we are assured that the extension produced by encapsulate is sound. In essence, the local definitions of the constrained functions are just ``witness functions'' that establish the consistency of the constraints. Because those definitions are local, they are not present in the theory produced by encapsulation. Encapsulate also exports all rules generated by its non-local events, but rules generated by local events are not exported.

The default-defun-mode for the first event in an encapsulation is the default defun-mode ``outside'' the encapsulation. But since events changing the defun-mode are permitted within the body of an encapsulate, the default defun-mode may be changed. However, defun-mode changes occurring within the body of the encapsulate are not exported. In particular, the acl2-defaults-table after an encapsulate is always the same as it was before the encapsulate, even though the encapsulate body might contain defun-mode changing events, :program and :logic. See defun-mode. More generally, after execution of an encapsulate event, the value of acl2-defaults-table is restored to what it was immediately before that event was executed. See acl2-defaults-table.

Theorems about the constrained function symbols may then be proved -- theorems whose proofs necessarily employ only the constraints. Thus, those theorems may be later functionally instantiated, as with the :functional-instance lemma instance (see lemma-instance), to derive analogous theorems about different functions, provided the constraints (see constraint) can be proved about the new functions.

We make some remarks on guards and evaluation. Calls of functions introduced in the signatures list cannot be evaluated in the ACL2 read-eval-print loop. See defattach for a way to overcome this limitation. Moreover, any :guard supplied in the signature is automatically associated in the world with its corresponding function symbol, with no requirement other than that the guard is a legal term all of whose function symbols are in :logic mode with their guards verified. In particular, there need not be any relationship between a guard in a signature and the guard in a local witness function. Finally, note that for functions introduced non-locally inside an encapsulate event, guard verification is illegal unless ACL2 determines that the proof obligations hold outside the encapsulate event as well.

(encapsulate
 ((f (x) t))
 (local (defun f (x) (declare (xargs :guard t)) (consp x)))
 ;; ERROR!
 (defun g (x)
   (declare (xargs :guard (f x)))
   (car x)))

Observe that if the signatures list is empty, encapsulate may still be useful for deriving theorems to be exported whose proofs require lemmas you prefer to hide (i.e., made local).

The order of the events in the vicinity of an encapsulate is confusing. We discuss it in some detail here because when logical names are being used with theory functions to compute sets of rules, it is sometimes important to know the order in which events were executed. (See logical-name and see theory-functions.) What, for example, is the set of function names extant in the middle of an encapsulation?

If the most recent event is previous and then you execute an encapsulate constraining an-element with two non-local events in its body, thm1 and thm2, then the order of the events after the encapsulation is (reading chronologically forward): previous, thm1, thm2, an-element (the encapsulate itself). Actually, between previous and thm1 certain extensions were made to the world by the superior encapsulate, to permit an-element to be used as a function symbol in thm1.

Finally, we note that an encapsulate event is redundant if and only if a syntactically identical encapsulate has already been executed under the same default-defun-mode, default-ruler-extenders, and default-verify-guards-eagerness. See redundant-events.