• Top
    • Documentation
    • Books
    • Boolean-reasoning
    • Projects
    • Debugging
    • Std
    • Proof-automation
    • Macro-libraries
    • ACL2
      • Theories
      • Rule-classes
      • Proof-builder
      • Recursion-and-induction
      • Hons-and-memoization
      • Events
      • Parallelism
      • History
      • Programming
        • Defun
        • Declare
        • System-utilities
        • Stobj
        • State
        • Mutual-recursion
        • Memoize
        • Mbe
        • Io
        • Defpkg
        • Apply$
        • Loop$
        • Programming-with-state
        • Arrays
        • Characters
        • Time$
        • Defmacro
        • Loop$-primer
          • Lp-section-8
          • Lp-section-10
          • Lp-section-6
          • Lp-section-9
          • Lp-section-17
          • Lp-section-16
          • Lp-section-15
          • Lp-section-11
          • Lp-section-4
          • Lp-section-7
            • Lp-section-13
            • Lp-section-12
            • Lp-section-14
            • Lp-section-5
            • Lp-section-0
            • Lp-section-2
            • Lp-section-18
            • Lp-section-3
            • Lp-section-1
          • Fast-alists
          • Defconst
          • Evaluation
          • Guard
          • Equality-variants
          • Compilation
          • Hons
          • ACL2-built-ins
          • Developers-guide
          • System-attachments
          • Advanced-features
          • Set-check-invariant-risk
          • Numbers
          • Efficiency
          • Irrelevant-formals
          • Introduction-to-programming-in-ACL2-for-those-who-know-lisp
          • Redefining-programs
          • Lists
          • Invariant-risk
          • Errors
          • Defabbrev
          • Conses
          • Alists
          • Set-register-invariant-risk
          • Strings
          • Program-wrapper
          • Get-internal-time
          • Basics
          • Packages
          • Oracle-eval
          • Defmacro-untouchable
          • <<
          • Primitive
          • Revert-world
          • Unmemoize
          • Set-duplicate-keys-action
          • Symbols
          • Def-list-constructor
          • Easy-simplify-term
          • Defiteration
          • Fake-oracle-eval
          • Defopen
          • Sleep
        • Operational-semantics
        • Real
        • Start-here
        • Debugging
        • Miscellaneous
        • Output-controls
        • Macros
        • Interfacing-tools
      • Interfacing-tools
      • Hardware-verification
      • Software-verification
      • Math
      • Testing-utilities
    • Loop$-primer

    Lp-section-7

    Using Loop$s and Guards in Defuns

    LP7: Using Loop$s and Guards in Defuns

    Loop$ statements are most efficient when they are guard verified. Loop$s typed at the top-level are typically not guard verified and so they are not executed by running compiled Common Lisp loops. Instead, their formal semantics is executed which is akin to saying they are interpreted. But execution efficiency was one of the motivational factors in the introduction of loop$ and that is best achieved by using loop$s in guard verified function definitions.

    Here is an example. We've already discussed member and the fact that calls of member can be replaced by loop$s. Member is a macro that expands logically to member-equal, where

    (defun member-equal (x lst)
      (declare (xargs :guard (true-listp lst)))
      (cond ((endp lst) nil)
            ((equal x (car lst)) lst)
            (t (member-equal x (cdr lst)))))

    Let's define an equivalent function, named member-equal-loop$, using loop$.

    (defun member-equal-loop$ (x lst)
      (declare (xargs :guard (true-listp lst)))
      (loop$ for tail of-type (satisfies true-listp) on lst
             thereis
             (if (equal x (car tail)) tail nil)))
    
    (defthm member-equal-loop$-is-member-equal
       (equal (member-equal-loop$ x lst)
              (member-equal x lst)))

    Since member-equal has a guard of (true-listp lst), we will give our member-equal-loop$ the same guard. But in our loop$ above we also wrote that the iteration variable tail satisfies true-listp. We do that so that ACL2 can prove the guard on (car tail) in the thereis clause. (We could change ACL2 to infer this type for tail in this special case, but more generally we would prefer to have an effective heuristic for transferring arbitrary properties of lst to relevant properties of tail.)

    The guard obligations for member-equal-loop$ are obscure because we haven't explained the formal semantics of loop$ yet. But we do not discuss the guard obligations of loop$s in the primer. If you define a function containing a loop$ and try to verify its guards the system will generate the necessary obligations and try to prove them. Our solutions to all the exercises in this primer can be guard verified automatically. For more details on FOR loop$ guards and the guard obligations they generate, see for-loop$, specifically, the sections “Special Guard Conjectures for FOR Loop$s” and “Discussion of Why Loop$s Have Special Guards.”

    After admitting member-equal-loop$ ACL2 can prove inductively that it is equal to member-equal. We'll come back to that later too.

    An alternative way to specify the type of tail would be to add a :guard rather than an of-type expression.

    (defun member-equal-loop$ (x lst)
      (declare (xargs :guard (true-listp lst)))
      (loop$ for tail on lst
             thereis
             :guard (true-listp tail)
             (if (equal x (car tail)) tail nil)))

    The :guard feature of ACL2 is more flexible than of-type because guards allow you to use multiple variables to express a constraint, while of-type implicitly limits the assertion to the variable being introduced. For example, with a guard you could say (subsetp-equal tail lst) while you cannot express such a constraint with of-type. However, of-type is understood by the Common Lisp compiler which might optimize the compiled code using that type information, while guards are not seen by the compiler.

    When you use a loop$ in a defun to be guard verified, be sure to constrain its iteration variables (and global variables) appropriately so you can verify the guards of the body. (You are also allowed to specify different guards for any when and until expressions.) We generally split our constraints between of-type and :guard to inform the compiler of simple types, while more elaborate guard conditions are sometimes necessary to verify the guards of the body, etc. ACL2 will take any of-type information and conjoin it to any :guard when doing guard verification.

    Now go to lp-section-8 (or return to the Table of Contents).