• 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-9

    Semantics of FOR Loop$s

    LP9: Semantics of FOR Loop$s

    For a thorough discussion of the semantics of FOR loop$s see for-loop$, specifically the section “Semantics” which includes two subsections, “Semantics of Simple Loop$s” and “Semantics of Fancy Loop$s.” “Fancy” FOR loop$s are FOR loop$s involving multiple iteration variables and/or use of “global” (i.e., non-iteration) variables in the loop$ body or the until or when clauses. We discuss DO loop$s later. In this section of the primer we just present some examples to drive home a few important points about FOR loop$s, namely

    • The semantics of a loop$ statement is obtained by translating the loop$ statement, as with the command :trans.
    • However, translation inserts a lot of tags into the formal term it produced. These tags allow us to execute loop$ statements more efficiently in the top-level ACL2 read-eval-print loop$. The tags on a translated loop$, term, can be removed by the :tc (“translate and clean”) command and its variants :tca and :tcp. When we exhibit semantics we typically show these simplified, equivalent terms produced by one of these commands.
    • The semantics of FOR loop$s look quite different from the semantics of DO loop$s. Furthermore, the semantics of simple loop$s have a different form than those of fancy loop$. So “minor” changes in the syntax of a loop$ statement — the addition of an AS clause, the use of global variable in the body of the loop$, or the use of DO instead of, say, collect — can cause radical changes in the form of the semantics.
    • The semantics of loop$s always involve calls of scions on lambda objects derived from the when, until, and loop$ body clauses. The scions for simple FOR loop$s are sum$, collect$, always$, thereis$, append$, until$, and when$; the scions for fancy FOR loop$s are sum$+, collect$+, always$+, thereis$+, append$+, until$+, and when$+; and the scion for DO loop$s is do$.
    • The iteration variables become formals of the lambda objects and standard names are used in place of the user's names.

    Now we drive home these points with some examples. The next section elaborates further.

    Here is the full, formal translation of a simple FOR loop$.

    ACL2 !>:trans (loop$ for x of-type integer in lst
                         when (evenp x)
                         collect (* x x))
    
    (RETURN-LAST
     'PROGN
     '(LOOP$ FOR X OF-TYPE INTEGER IN LST
             WHEN (EVENP X)
             COLLECT (* X X))
     (COLLECT$
       '(LAMBDA (LOOP$-IVAR)
                (DECLARE (TYPE INTEGER LOOP$-IVAR)
                         (XARGS :GUARD (INTEGERP LOOP$-IVAR)
                                :SPLIT-TYPES T)
                         (IGNORABLE LOOP$-IVAR))
                (RETURN-LAST 'PROGN
                             '(LAMBDA$ (LOOP$-IVAR)
                                       (DECLARE (TYPE INTEGER LOOP$-IVAR))
                                       (LET ((X LOOP$-IVAR))
                                            (DECLARE (IGNORABLE X))
                                            (* X X)))
                             ((LAMBDA (X) (BINARY-* X X))
                              LOOP$-IVAR)))
       (WHEN$ '(LAMBDA (LOOP$-IVAR)
                       (DECLARE (TYPE INTEGER LOOP$-IVAR)
                                (XARGS :GUARD (INTEGERP LOOP$-IVAR)
                                       :SPLIT-TYPES T)
                                (IGNORABLE LOOP$-IVAR))
                       (RETURN-LAST 'PROGN
                                    '(LAMBDA$ (LOOP$-IVAR)
                                              (DECLARE (TYPE INTEGER LOOP$-IVAR))
                                              (LET ((X LOOP$-IVAR))
                                                   (DECLARE (IGNORABLE X))
                                                   (EVENP X)))
                                    ((LAMBDA (X) (EVENP X)) LOOP$-IVAR)))
              LST)))

    However, the logical meaning of this is easier to understand by looking at one of the tc variations.

    ACL2 !>:tc (loop$ for x of-type integer in lst
                          when (evenp x)
                          collect (* x x))
     (COLLECT$ '(LAMBDA (LOOP$-IVAR)
                        (BINARY-* LOOP$-IVAR LOOP$-IVAR))
               (WHEN$ '(LAMBDA (LOOP$-IVAR) (EVENP LOOP$-IVAR))
                      LST))
    ACL2 !>:tcp (loop$ for x of-type integer in lst
                           when (evenp x)
                           collect (* x x))
     (COLLECT$ (LAMBDA$ (LOOP$-IVAR)
                        (* LOOP$-IVAR LOOP$-IVAR))
               (WHEN$ (LAMBDA$ (LOOP$-IVAR)
                               (EVENP LOOP$-IVAR))
                      LST))
    ACL2 !>:tca (loop$ for x of-type integer in lst
                           when (evenp x)
                           collect (* x x))
     (PROG2$
       '(LOOP$ FOR X OF-TYPE INTEGER IN LST
               WHEN (EVENP X)
               COLLECT (* X X))
       (COLLECT$ (LAMBDA$ (LOOP$-IVAR)
                    (DECLARE (TYPE INTEGER LOOP$-IVAR)
                             (XARGS :GUARD (INTEGERP LOOP$-IVAR)
                                    :SPLIT-TYPES T))
                    (LET ((X LOOP$-IVAR))
                         (* X X)))
                 (WHEN$ (LAMBDA$ (LOOP$-IVAR)
                           (DECLARE (TYPE INTEGER LOOP$-IVAR)
                                    (XARGS :GUARD (INTEGERP LOOP$-IVAR)
                                           :SPLIT-TYPES T))
                           (LET ((X LOOP$-IVAR))
                                (EVENP X)))
                        LST)))

    Note that :tc prints the term generated by translating the loop$ and then removing all tags. The guard and type declarations are logically irrelevant. What you see is what the theorem prover will see (if all the necessary warrants are assumed) before it starts to rewrite the term. Note that the lambda object is just a quoted list constant and the body has been translated so that the * macro was expanded in terms of binary-*.

    :Tcp (“p” for “pretty”) introduces familiar system macros.

    :Tca (“a” for “annotations”) includes the original loop$ statement and shows declarations (if any) and the correspondence between the user's iteration variable (x here) and the formal variable used in the lambda objects generated (loop$-ivar here).

    The translation of loop$ bodies into lambda objects standardizes the variable name, either as loop$-ivar for simple loop$s which have just one iteration variable, or as loop$-gvars and loop$-ivars for fancy loop$s which may have one or more global variables and one or more iteration variables. We'll deal with fancy loop$s immediately below, but values are passed for loop$-gvars and loop$-ivars as tuples listing all the global values and all the current iteration variable values.

    The advantage of this standardization is that choosing different names for the iteration variables does not affect the formal semantics.

    However, if we make a simple syntactic change, like introducing a global variable by changing the body of the loop$ from (* x x) to (* x a), where a is not an iteration variable, we drastically change the semantics because we've converted the simple loop$ into a fancy loop$. The (clean and pretty) semantics of

    (loop$ for x in lst when (evenp x) collect (* x a))

    is

    (COLLECT$+ (LAMBDA$ (LOOP$-GVARS LOOP$-IVARS)
                        (* (CAR LOOP$-IVARS) (CAR LOOP$-GVARS)))
               (LIST A)
               (WHEN$+ (LAMBDA$ (LOOP$-GVARS LOOP$-IVARS)
                                (EVENP (CAR LOOP$-IVARS)))
                       NIL
                       (LOOP$-AS (LIST LST))))

    The annotated semantics of that loop$ is

    (PROG2$
     '(LOOP$ FOR X IN LST
             WHEN (EVENP X)
             COLLECT (* X A))
     (COLLECT$+
       (LAMBDA$ (LOOP$-GVARS LOOP$-IVARS)
                (DECLARE (XARGS :GUARD (AND (TRUE-LISTP LOOP$-GVARS)
                                            (EQUAL (LEN LOOP$-GVARS) 1)
                                            (TRUE-LISTP LOOP$-IVARS)
                                            (EQUAL (LEN LOOP$-IVARS) 1))
                                :SPLIT-TYPES T))
                (LET ((A (CAR LOOP$-GVARS))
                      (X (CAR LOOP$-IVARS)))
                     (* X A)))
       (LIST A)
       (WHEN$+ (LAMBDA$ (LOOP$-GVARS LOOP$-IVARS)
                        (DECLARE (XARGS :GUARD (AND (TRUE-LISTP LOOP$-GVARS)
                                                    (EQUAL (LEN LOOP$-GVARS) 0)
                                                    (TRUE-LISTP LOOP$-IVARS)
                                                    (EQUAL (LEN LOOP$-IVARS) 1))
                                        :SPLIT-TYPES T))
                        (LET ((X (CAR LOOP$-IVARS)))
                             (EVENP X)))
               NIL
               (LOOP$-AS (LIST LST)))))

    Logically, we could have typed either of the above terms instead of the fancy loop$. But operationally, the loop$ executes faster (when guards are verified).

    Notice that the simple scions COLLECT$ and WHEN$ have been replaced by their fancy counterparts, COLLECT$+ and WHEN$+. The lambda objects now have two formals, one for holding a tuple containing all of the global variable values and one for a tuple holding all of the current iteration variable values. The global variable value tuple, (LIST A), is passed into the fancy scions if the corresponding lambda object uses globals (as for the collect$+ but not for the when$+ lambda where NIL is passed instead). The list over which the iteration variable ranges, LST, is now a list of tuples constructed by LOOP$-AS. Finally, the bodies of the lambdas now access the relevant values with CAR and CDR nests around the global and iteration tuples. (No CDRs appear above because there is only one global and one iteration variable in this example.)

    The following similar but still fancier loop$ illustrates the general situation. This loop$ has two iteration variables, x and y taking on values from two ranges, xlst, and ylst, and uses two global variables, a and b, in the body. We've added comments to name the components of the two tuples.

    ACL2 !>:tcp (loop$ for x in xlst
                       as  y in ylst
                       when (evenp x)
                       collect (* x y a b))
     (COLLECT$+ (LAMBDA$ (LOOP$-GVARS LOOP$-IVARS)
                         (* (CAR LOOP$-IVARS)               ; x
                            (CADR LOOP$-IVARS)              ; y
                            (CAR LOOP$-GVARS)               ; a
                            (CADR LOOP$-GVARS)))            ; b
                (LIST A B)
                (WHEN$+ (LAMBDA$ (LOOP$-GVARS LOOP$-IVARS)
                                 (EVENP (CAR LOOP$-IVARS))) ; x
                        NIL
                        (LOOP$-AS (LIST XLST YLST))))

    Notice how the bodies of the lambda objects now use components of the loop$-gvars and loop$-ivars) for the current values of the global variables a and b and the iteration variables x and y. The values of x and y are corresponding elements of xlst and ylst as grouped together by (loop$-as (list xlst ylst)).

    For a still-more elaborate FOR loop$ and a step-by-step description of how the value of the formal semantics is computed, go to lp-section-10.

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