• 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
      • Operational-semantics
      • Real
      • Start-here
      • Debugging
      • Miscellaneous
        • Term
          • Lambda
            • Pseudo-termp
            • Term-order
            • Pseudo-term-listp
            • Guard-holders
            • Termp
            • Termify
            • L<
            • Kwote
            • Kwote-lst
          • Ld
          • Hints
          • Type-set
          • Ordinals
          • Clause
          • ACL2-customization
          • With-prover-step-limit
          • Set-prover-step-limit
          • With-prover-time-limit
          • Local-incompatibility
          • Set-case-split-limitations
          • Subversive-recursions
          • Specious-simplification
          • Defsum
          • Gcl
          • Oracle-timelimit
          • Thm
          • Defopener
          • Case-split-limitations
          • Set-gc-strategy
          • Default-defun-mode
          • Top-level
          • Reader
          • Ttags-seen
          • Adviser
          • Ttree
          • Abort-soft
          • Defsums
          • Gc$
          • With-timeout
          • Coi-debug::fail
          • Expander
          • Gc-strategy
          • Coi-debug::assert
          • Sin-cos
          • Def::doc
          • Syntax
          • Subversive-inductions
        • Output-controls
        • Macros
        • Interfacing-tools
      • Interfacing-tools
      • Hardware-verification
      • Software-verification
      • Math
      • Testing-utilities
    • Term
    • Apply$

    Lambda

    Lambda expressions, LAMBDA objects, and lambda$ expressions

    The word ``lambda'' occurs in several different contexts in ACL2. When we are being precise our meanings are as outlined below.

    • lambda expression -- This phrase is used to describe the syntactic entity beginning with the symbol lambda that is allowed to occupy the ``function'' position in an ACL2 term. Lambda expressions are most often created when let expressions are translated into their formal counterparts. We provide an example below.
    • LAMBDA object -- An ACL2 list constant interpreted as a ``function'' by apply$. LAMBDA objects may be written in terms by quoting them. However, we urge the user to introduce LAMBDA objects into terms by using the built-in macro lambda$. We provide examples below.
    • lambda$ expressions -- These are untranslated terms beginning with the macro symbol lambda$. They expand during translation to quoted LAMBDA objects. We provide examples below.

    These three phrases are very similar but mean very different things. You should read carefully when you see us talk about lambda things! Unfortunately, we're not always as precise as we might be so you might have to disambiguate our usage by context. If you see places in the documentation where you think we've messed up, please bring them to our attention!

    About Lambda Expressions

    Consider the function odd-evenp, defined with

    (defun odd-evenp (x)
      (if (zp x)
          -1
          (let ((ans (odd-evenp (- x 1))))
            (* (+ ans 1) (- ans 1)))))

    Distracting Aside: Can you see why we gave this function this name? Hint: We might have named it ``weird evenp.''

    The translated body of odd-evenp is

    (if (zp x)
        '-1
        ((lambda (ans)
                 (binary-* (binary-+ ans '1)
                           (binary-+ '-1 ans)))
         (odd-evenp (binary-+ '-1 x))))

    The syntactic entity in the function position of the term in the false branch, namely

    (lambda (ans)
      (binary-* (binary-+ ans '1)
                (binary-+ '-1 ans)))

    is a lambda expression.

    Lambda expressions are integral to the formal representation of terms. They are the formal mechanism by which local variables are introduced and thus allow repeated references to intermediate results without causing recomputation. In ACL2 they obey the rules of Common Lisp. In particular, while defining a recursive function it is allowed to call the function recursively within the lambda expression, e.g., to temporarily save the value of a recursive call for repeated use. (This is not illustrated by odd-evenp where the recursive call is outside of the lambda expression.) For more details on the formal representation of ACL2 terms, see term.

    About LAMBDA Objects

    Prior to Version 8.0 when apply$ was introduced, ``lambda expression'' was the only phrase the ACL2 developers used mentioning the word ``lambda.'' Some Community Books introduced various terms or objects mentioning the word but that is beyond the scope of this documentation. Because ``lambda'' occurred in no other context before apply$ we are not confident that every reference to what we are now calling ``lambda expressions'' were called by that precise phrase in old documentation. If you see a place where we refer to these entities by another phrase, please let us know!

    When apply$ was introduced, LAMBDA objects became a formally supported concept in the ACL2 implementation and we started using ``LAMBDA objects'' to refer to them. A LAMBDA object is generally a list, either of the form (LAMBDA vars body) or (LAMBDA vars dcl body). There are additional restrictions on vars, dcl, and body that we discuss later. But apply$ treats any consp object and tries to extract those components by rudimentary pattern matching. An example of a LAMBDA object is the list of length three (LAMBDA (x) (BINARY-+ '1 X)).

    Generally speaking when LAMBDA objects occur in translated terms they are quoted, as in

    (collect$ '(LAMBDA (X) (BINARY-+ '1 X)) lst)

    To highlight the fact that these objects are constants, we try to write them in UPPERCASE and typewriter font in this documentation. For the same reason, we generally write ``LAMBDA object'' rather than ``lambda object.''

    But of course there's no difference between the symbol LAMBDA and the symbol lambda. Furthermore, LAMBDA objects need not be quoted. From a logical perspective they could just be consed up because they are just ordinary ACL2 list constants. Their ``lambda'' status comes from being treated as functions by apply$. So one could write

    (collect$ (list 'lambda '(x) '(binary-+ '1 x)) lst)

    and we would say that the value of the term in the first argument of collect$ is a LAMBDA object.

    Beware, however, that consing up LAMBDA objects defeats the ilk analysis in defwarrant and the tameness analysis of apply$ and hence prevents functions containing such terms from being apply$d.

    According to the definitional axiom defining apply$, any object satisfying consp is treated as a LAMBDA object. Apply$ uses ``accessor'' functions to extract the ``formals'' and ``body'' of the object and proceeds to ev$ the body in an alist binding the formals. But ev$ insists that the expression object being evaluated be tame or else assigns it a default value. This insistence on tameness is due to fundamental logical reasons; otherwise, apply$ would allow us to prove NIL. So axiomatically apply$ operates as ``naively expected'' only on tamep-lambdap objects. One consequence of this, which we expect will be a minor inconvenience, is that unlike ACL2's lambda expressions apply$'s LAMBDA objects, when used in definitions of new functions, may not include recursive calls of the function being defined because they fail the tameness test.

    But wait! There's more. Execution efficiency of apply$ imposes some non-logical restrictions. These restrictions come from ACL2's execution story with respect to Common Lisp, and from the Common Lisp compiler. To execute LAMBDA objects most efficiently they must be well-formed, which is a concept even stronger than tameness. Among other requirements, well-formed LAMBDA objects obey the ACL2 and Common Lisp rules on variable names (not every symbol is a legal variable), on the use of free variables, on the body being a fully translated formal term, that the declarations, if any, be meaningful to the Common Lisp compiler, etc. You can read about well-formedness in well-formed-lambda-objectp if you want, but we don't encourage beginners to go there!

    Note: Even well-formedness is not enough to guarantee execution of compiled code. The LAMBDA object must also be guard verified (see verify-guards for a discussion) and its guard must be satisfied by the arguments to which it is applied.

    Note: A peculiar aspect of LAMBDA objects is that they can be written as legal ACL2 constants before they are well-formed LAMBDA objects, e.g., by referring to undefined functions, :program mode functions, unbadged functions, etc. They are, after all, just arbitrary quoted objects and any value in ACL2 can be quoted. An ill-formed LAMBDA object can become well-formed if the world is appropriately extended, e.g., the appropriate defuns or defwarrants are made. Perhaps worse, they can be well-formed and then become ill-formed by an undo. So at runtime apply$ has to check that the function symbol or LAMBDA object is appropriate. There is a sophisticated cache behind the execution machinery for LAMBDA objects in the evaluation theory. See print-cl-cache.

    About Lambda$ Expressions

    Rather than force users to type well-formed LAMBDA objects as quoted constants, ACL2 provides a macro allowing you to enter LAMBDA objects by typing something that looks like a lambda expression but which is properly translated and generates well-formed results (or causes a translation error).

    That macro -- which is not really a defined macro but is built into ACL2's translation mechanism -- is called lambda$ and uses of it in terms are called ``lambda$ expressions.'' Lambda$ expressions may only be used in argument slots of ilk :FN.

    An example of a lambda$ expression is the first argument of collect$ in

    (collect$ (lambda$ (x) (+ 1 x)) lst)

    That lambda$ expression essentially translates to the quoted well-formed LAMBDA object

    '(LAMBDA (X) (BINARY-+ '1 X))

    Note that the body is fully translated, unlike its appearance in the lambda$ expression. We say ``essentially'' because lambda$ always add a (DECLARE (IGNORABLE v1 ... vn)) that includes every formal. In addition, except when translating in theorems, lambda$ tags the translated body with a return-last expression to indicate it came from a lambda$. Despite these differences, the meaning of the translated lambda$ is the simple quoted LAMBDA object shown.

    Lambda$ expressions never appear in a fully translated term. All the lambda$ objects will have been translated into quoted LAMBDA objects.

    The body of a lambda$ expression must return a single value that is neither a stobj nor a df. The following example illustrates this point.

    (defun$ f1 (x)
      (declare (xargs :guard t))
      (mv x x))
    
    ; ERROR!  The body of the lambda$ returns two values.
    (defun f2 (y)
      (declare (xargs :guard t))
      (apply$ (lambda$ (x) (f1 x))
              (list y)))
    
    ; Succeeds.
    (defun f2 (y) (declare (xargs :guard t))
      (apply$ '(lambda (x) (f1 x))
              (list y)))

    Finally, to see how a lambda$ expression translates, see translam.