SRFI 1:   List Library
SRFI 2:   AND-LET*:   an AND with local bindings...
SRFI 4:   Homogeneous numeric vector datatypes
SRFI 5:   A compatible let form with signatures and rest arguments
SRFI 6:   Basic String Ports
SRFI 7:   Feature-based program configuration language
SRFI 8:   RECEIVE:   Binding to multiple values
SRFI 9:   Defining Record Types
SRFI 11:   Syntax for receiving multiple values
SRFI 13:   String Libraries
SRFI 14:   Character-set Library
SRFI 16:   Syntax for procedures of variable arity
SRFI 17:   Generalized set!
SRFI 19:   Time Data Types and Procedures
SRFI 23:   Error reporting mechanism
SRFI 25:   Multi-dimensional Array Primitives
SRFI 26:   Notation for Specializing Parameters without Currying
SRFI 27:   Sources of Random Bits
SRFI 28:   Basic Format Strings
SRFI 29:   Localization
SRFI 30:   Nested Multi-line Comments
SRFI 31:   A special form rec for recursive evaluation
SRFI 34:   Exception Handling for Programs
SRFI 35:   Conditions
SRFI 38:   External Representation for Data With Shared Structure
SRFI 39:   Parameter objects
SRFI 40:   A Library of Streams
SRFI 41:   Streams
SRFI 42:   Eager Comprehensions
SRFI 43:   Vector Library
SRFI 45:   Primitives for Expressing Iterative Lazy Algorithms
SRFI 48:   Intermediate Format Strings
SRFI 54:   Formatting
SRFI 57:   Records
SRFI 59:   Vicinity
SRFI 60:   Integers as Bits
SRFI 61:   A more general cond clause
SRFI 62:   S-expression comments
SRFI 63:   Homogeneous and Heterogeneous Arrays
SRFI 64:   A Scheme API for test suites
SRFI 66:   Octet Vectors
SRFI 67:   Compare Procedures
SRFI 69:   Basic hash tables
SRFI 71:   Extended LET-syntax for multiple values
SRFI 74:   Octet-Addressed Binary Blocks
SRFI 78:   Lightweight testing
SRFI 86:   MU & NU simulating VALUES & CALL-WITH-VALUES...
SRFI 87:   => in case clauses
SRFI 98:   An interface to access environment variables
On this page:

SRFI 5: A compatible let form with signatures and rest arguments🔗

 (require srfi/5) package: srfi-lib

Original specification: SRFI 5

For historical reasons, the SRFI 5 specification document has a restrictive license and is not included in the main Racket distribution.

The implementation in srfi/5 and this documentation are distributed under the same license as Racket: only the original specification document is restrictively licensed.


(let ([id init-expr] ...)
  body ...+)
(let ([id init-expr] ...+ rest-binding)
  body ...+)
(let loop-id ([id init-expr] ... maybe-rest-binding)
  body ...+)
(let (loop-id [id init-expr] ... maybe-rest-binding)
  body ...+)
maybe-rest-binding = 
  | rest-binding
rest-binding = rest-id rest-init-expr ...
Like let from racket/base, but extended to support additional variants of named let.

As with let from racket/base, SRFI 5’s let form conceptually expands to the immediate application of a function to the values of the init-exprs: the ids are bound in the bodys (but not in any init-exprs or rest-init-exprs), and loop-id, if present, is bound in the bodys to the function itself, allowing it to be used recursively. An id or a rest-id can shadow loop-id, but the rest-id (if given) and all iss much be distinct.

SRFI 5’s let adds support for a syntax like define’s function shorthand, which allows the bindings to be written in a syntax resembling an application of the function bound to loop-id.

Additionally, SRFI 5’s let adds support for rest arguments. If a rest-id is present, the function bound to loop-id (or the conceptual anonymous function, if loop-id is not used) will accept an unlimited number of additional arguments after its required by-position arguments, and the rest-id will be bound in the bodys (but not in any init-exprs or rest-init-exprs) to a list of those additional arguments. The values of the rest-init-exprs are supplied as arguments to the initial, implicit application when the let form is evaluated, so the initial value bound to rest-id is (list rest-init-expr ...).

Unlike the kw-formals of lambda and define or the formals of case-lambda, the bindings of SRFI 5’s let, with or without a rest-binding, are always a proper (syntactic) list.

A rest-binding can be used with both the define-like and the named-let–like variants of let. It is also possible to use rest-id without any loop-id; however, as specified in the grammar, at least one idinit-expr pair is required in that case. (Otherwise, there would be an ambiguity with the define-like variant).

; define-like bindings
> (define (factorial n)
    (let (fact [n n] [acc 1])
      (if (zero? n)
          (fact (sub1 n) (* n acc)))))
> (factorial 5)


> (factorial 11)


; rest arguments with named-let--like bindings
> (let reverse-onto ([lst '(a b c)]
    (if (null? lst)
        (apply reverse-onto (cdr lst) (car lst) tail)))

'(c b a)

> (let reverse-onto ([lst '(a b c)]
                     tail 'x 'y 'z)
    (if (null? lst)
        (apply reverse-onto (cdr lst) (car lst) tail)))

'(c b a x y z)

> (let no-evens (lst 1 2 3 4 5)
      [(null? lst)
      [(even? (car lst))
       (apply no-evens (cdr lst))]
       (cons (car lst) (apply no-evens (cdr lst)))]))

'(1 3 5)

; rest arguments with define-like bindings
> (let (reverse-onto [lst '(a b c)] tail)
    (if (null? lst)
        (apply reverse-onto (cdr lst) (car lst) tail)))

'(c b a)

> (let (reverse-onto [lst '(a b c)]    tail 'x 'y 'z)
    (if (null? lst)
        (apply reverse-onto (cdr lst) (car lst) tail)))

'(c b a x y z)

> (let (loop [continue? 0] args 'a 'a1 'a2)
    (case continue?
      [(0) (cons args (loop 1 'b))]
      [(1) (cons args (loop 2 'c 'd))]
      [else (list args)]))

'((a a1 a2) (b) (c d))

; rest arguments without any loop-id
> (let ([x 1]
        [y 2]
        z 3 4 5 6 7)
    (list* x y z))

'(1 2 3 4 5 6 7)