💾 Archived View for shaggypeak.com › library › r7rs › section4.2.2.gmi captured on 2024-05-10 at 12:59:18. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2023-03-20)

-=-=-=-=-=-=-

Table of Contents

Section 4.2.1 - Conditionals

Section 4.2.3 - Sequencing

4.2.2 Binding constructs

The binding constructs let, let*, letrec, letrec*, let-values, and let*-values give Scheme a block structure, like Algol 60. The syntax of the first four constructs is identical, but they differ in the regions they establish for their variable bindings. In a let expression, the initial values are computed before any of the variables become bound; in a let* expression, the bindings and evaluations are performed sequentially; while in letrec and letrec* expressions, all the bindings are in effect while their initial values are being computed, thus allowing mutually recursive definitions. The let-values and let*-values constructs are analogous to let and let* respectively, but are designed to handle multiple-valued expressions, binding different identifiers to the returned values.

(let <bindings> <body>)

Syntax: <Bindings> has the form

((<variable1> <init1>) ...),

where each <init> is an expression, and <body> is a sequence of zero or more definitions followed by a sequence of one or more expressions as described in section 4.1.4. It is an error for a <variable> to appear more than once in the list of variables being bound.

Semantics: The <init>s are evaluated in the current environment (in some unspecified order), the <variable>s are bound to fresh locations holding the results, the <body> is evaluated in the extended environment, and the values of the last expression of <body> are returned. Each binding of a <variable> has <body> as its region.

(let ((x 2) (y 3))
  (* x y))  ⇒ 6

(let ((x 2) (y 3))
  (let ((x 7)
        (z (+ x y)))
    (* z x)))  ⇒ 35

See also “named let,” section 4.2.4.

(let* <bindings> <body>)

Syntax: <Bindings> has the form

((<variable1> <init1>) ...),

and <body> is a sequence of zero or more definitions followed by one or more expressions as described in section 4.1.4.

Semantics: The let* binding construct is similar to let, but the bindings are performed sequentially from left to right, and the region of a binding indicated by (<variable><init>) is that part of the let* expression to the right of the binding. Thus the second binding is done in an environment in which the first binding is visible, and so on. The <variable>s need not be distinct.

(let ((x 2) (y 3))
  (let* ((x 7)
         (z (+ x y)))
    (* z x)))  ⇒ 70
(letrec <bindings> <body>)

Syntax: <Bindings> has the form

((<variable1> <init1>) ...),

and <body> is a sequence of zero or more definitions followed by one or more expressions as described in section 4.1.4. It is an error for a <variable> to appear more than once in the list of variables being bound.

Semantics: The <variable>s are bound to fresh locations holding unspecified values, the <init>s are evaluated in the resulting environment (in some unspecified order), each <variable> is assigned to the result of the corresponding <init>, the <body> is evaluated in the resulting environment, and the values of the last expression in <body> are returned. Each binding of a <variable> has the entire letrec expression as its region, making it possible to define mutually recursive procedures.

(letrec ((even?
           (lambda (n)
             (if (zero? n) #t
                 (odd? (- n 1)))))
         (odd?
           (lambda (n)
             (if (zero? n) #f
                 (even? (- n 1))))))
  (even? 88))  ⇒ #t

One restriction on letrec is very important: if it is not possible to evaluate each <init> without assigning or referring to the value of any <variable>, it is an error. The restriction is necessary because letrec is defined in terms of a procedure call where a lambda expression binds the <variable>s to the values of the <init>s. In the most common uses of letrec, all the <init>s are lambda expressions and the restriction is satisfied automatically.

(letrec* <bindings> <body>)

Syntax: <Bindings> has the form

((<variable1> <init1>) ...),

and <body> is a sequence of zero or more definitions followed by one or more expressions as described in section 4.1.4. It is an error for a <variable> to appear more than once in the list of variables being bound.

Semantics: The <variable>s are bound to fresh locations, each <variable> is assigned in left-to-right order to the result of evaluating the corresponding <init>, the <body> is evaluated in the resulting environment, and the values of the last expression in <body> are returned. Despite the left-to-right evaluation and assignment order, each binding of a <variable> has the entire letrec* expression as its region, making it possible to define mutually recursive procedures.

If it is not possible to evaluate each <init> without assigning or referring to the value of the corresponding <variable> or the <variable> of any of the bindings that follow it in <bindings>, it is an error. Another restriction is that it is an error to invoke the continuation of an <init> more than once.

(letrec* ((p
            (lambda (x)
              (+ 1 (q (- x 1)))))
          (q
            (lambda (y)
              (if (zero? y) 0
                  (+ 1 (p (- y 1))))))
          (x (p 5))
          (y x))
  y)  ⇒ 5
(let-values <mv binding spec> <body>)

Syntax: <Mv binding spec> has the form

((<formals1> <init1>) ...),

where each <init> is an expression, and <body> is zero or more definitions followed by a sequence of one or more expressions as described in section 4.1.4. It is an error for a variable to appear more than once in the set of <formals>.

Semantics: The <init>s are evaluated in the current environment (in some unspecified order) as if by invoking call-with-values, and the variables occurring in the <formals> are bound to fresh locations holding the values returned by the <init>s, where the <formals> are matched to the return values in the same way that the <formals> in a lambda expression are matched to the arguments in a procedure call. Then, the <body> is evaluated in the extended environment, and the values of the last expression of <body> are returned. Each binding of a <variable> has <body> as its region.

It is an error if the <formals> do not match the number of values returned by the corresponding <init>.

(let-values (((root rem) (exact-integer-sqrt 32)))
  (* root rem))  ⇒ 35
(let*-values <mv binding spec> <body>)

Syntax: <Mv binding spec> has the form

((<formals> <init>) ...),

and <body> is a sequence of zero or more definitions followed by one or more expressions as described in section 4.1.4. In each <formals>, it is an error if any variable appears more than once.

Semantics: The let*-values construct is similar to let-values, but the <init>s are evaluated and bindings created sequentially from left to right, with the region of the bindings of each <formals> including the <init>s to its right as well as <body>. Thus the second <init> is evaluated in an environment in which the first set of bindings is visible and initialized, and so on.

(let ((a ’a) (b ’b) (x ’x) (y ’y))
  (let*-values (((a b) (values x y))
                ((x y) (values a b)))
    (list a b x y)))  ⇒ (x y x y)