You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
typesetting/quad/qtest/mds/forms.md

78 KiB

Expressions and Definitions

The [missing] chapter introduced some of Rackets syntactic forms: definitions, procedure applications, conditionals, and so on. This section provides more details on those forms, plus a few additional basic forms.

1 Notation                                                         
                                                                   
2 Identifiers and Binding                                          
                                                                   
3 Function Calls \(Procedure Applications\)                        
  3.1 Evaluation Order and Arity                                   
  3.2 Keyword Arguments                                            
  3.3 The `apply` Function                                         
                                                                   
4 Functions \(Procedures\): `lambda`                               
  4.1 Declaring a Rest Argument                                    
  4.2 Declaring Optional Arguments                                 
  4.3 Declaring Keyword Arguments                                  
  4.4 Arity-Sensitive Functions: `case-lambda`                     
                                                                   
5 Definitions: `define`                                            
  5.1 Function Shorthand                                           
  5.2 Curried Function Shorthand                                   
  5.3 Multiple Values and `define-values`                          
  5.4 Internal Definitions                                         
                                                                   
6 Local Binding                                                    
  6.1 Parallel Binding: `let`                                      
  6.2 Sequential Binding: `let*`                                   
  6.3 Recursive Binding: `letrec`                                  
  6.4 Named `let`                                                  
  6.5 Multiple Values: `let-values`, `let*-values`, `letrec-values`
                                                                   
7 Conditionals                                                     
  7.1 Simple Branching: `if`                                       
  7.2 Combining Tests: `and` and `or`                              
  7.3 Chaining Tests: `cond`                                       
                                                                   
8 Sequencing                                                       
  8.1 Effects Before: `begin`                                      
  8.2 Effects After: `begin0`                                      
  8.3 Effects If...: `when` and `unless`                           
                                                                   
9 Assignment: `set!`                                               
  9.1 Guidelines for Using Assignment                              
  9.2 Multiple Values: `set!-values`                               
                                                                   
10 Quoting: `quote` and `'`                                        
                                                                   
11 Quasiquoting: `quasiquote` and ``                              
                                                                   
12 Simple Dispatch: `case`                                         
                                                                   
13 Dynamic Binding: `parameterize`                                 

1. Notation

This chapter and the rest of the documentation uses a slightly different notation than the character-based grammars of the [missing] chapter. The grammar for a use of a syntactic form something is shown like this:

(something [id ...+] an-expr ...)

The italicized meta-variables in this specification, such as id and an-expr, use the syntax of Racket identifiers, so an-expr is one meta-variable. A naming convention implicitly defines the meaning of many meta-variables:

  • A meta-variable that ends in id stands for an identifier, such as x or my-favorite-martian.

  • A meta-identifier that ends in keyword stands for a keyword, such as #:tag.

  • A meta-identifier that ends with expr stands for any sub-form, and it will be parsed as an expression.

  • A meta-identifier that ends with body stands for any sub-form; it will be parsed as either a local definition or an expression. A body can parse as a definition only if it is not preceded by any expression, and the last body must be an expression; see also Internal Definitions.

Square brackets in the grammar indicate a parenthesized sequence of forms, where square brackets are normally used by convention. That is, square brackets do not mean optional parts of the syntactic form.

A ... indicates zero or more repetitions of the preceding form, and ...+ indicates one or more repetitions of the preceding datum. Otherwise, non-italicized identifiers stand for themselves.

Based on the above grammar, then, here are a few conforming uses of something:

(something [x])                                 
(something [x] (+ 1 2))                         
(something [x my-favorite-martian x] (+ 1 2) #f)

Some syntactic-form specifications refer to meta-variables that are not implicitly defined and not previously defined. Such meta-variables are defined after the main form, using a BNF-like format for alternatives:

(something-else [thing ...+] an-expr ...)
                                         
thing = thing-id                         
      | thing-keyword                    

The above example says that, within a something-else form, a thing is either an identifier or a keyword.

2. Identifiers and Binding

The context of an expression determines the meaning of identifiers that appear in the expression. In particular, starting a module with the language racket, as in

#lang racket

means that, within the module, the identifiers described in this guide start with the meaning described here: cons refers to the function that creates a pair, car refers to the function that extracts the first element of a pair, and so on.

+[missing] introduces the syntax of identifiers.

Forms like define, lambda, and let associate a meaning with one or more identifiers; that is, they bind identifiers. The part of the program for which the binding applies is the scope of the binding. The set of bindings in effect for a given expression is the expressions environment.

For example, in

#lang racket    
                
(define f       
  (lambda (x)   
    (let ([y 5])
      (+ x y))))
                
(f 10)          

the define is a binding of f, the lambda has a binding for x, and the let has a binding for y. The scope of the binding for f is the entire module; the scope of the x binding is (let ([y 5]) (+ x y)); and the scope of the y binding is just (+ x y). The environment of (+ x y) includes bindings for y, x, and f, as well as everything in racket.

A module-level define can bind only identifiers that are not already defined or required into the module. A local define or other binding forms, however, can give a new local binding for an identifier that already has a binding; such a binding shadows the existing binding.

Examples:

(define f                                    
  (lambda (append)                           
    (define cons (append "ugly" "confusing"))
    (let ([append 'this-was])                
      (list append cons))))                  
                                             
> (f list)                                   
'(this-was ("ugly" "confusing"))             

Similarly, a module-level define can shadow a binding from the modules language. For example, (define cons 1) in a racket module shadows the cons that is provided by racket. Intentionally shadowing a language binding is rarely a good idea—especially for widely used bindings like cons—but shadowing relieves a programmer from having to avoid every obscure binding that is provided by a language.

Even identifiers like define and lambda get their meanings from bindings, though they have transformer bindings (which means that they indicate syntactic forms) instead of value bindings. Since define has a transformer binding, the identifier define cannot be used by itself to get a value. However, the normal binding for define can be shadowed.

Examples:

> define                    
eval:1:0: define: bad syntax
  in: define                
> (let ([define 5]) define) 
5                           

Again, shadowing standard bindings in this way is rarely a good idea, but the possibility is an inherent part of Rackets flexibility.

3. Function Calls Procedure Applications

An expression of the form

(proc-expr arg-expr ...)

is a function call—also known as a procedure application—when proc-expr is not an identifier that is bound as a syntax transformer such as `if` or `define`.

3.1. Evaluation Order and Arity

A function call is evaluated by first evaluating the proc-expr and all arg-exprs in order left to right. Then, if proc-expr produces a function that accepts as many arguments as supplied arg-exprs, the function is called. Otherwise, an exception is raised.

Examples:

> (cons 1 null)                                           
'(1)                                                      
> (+ 1 2 3)                                               
6                                                         
> (cons 1 2 3)                                            
cons: arity mismatch;                                     
 the expected number of arguments does not match the given
number                                                    
  expected: 2                                             
  given: 3                                                
  arguments...:                                           
   1                                                      
   2                                                      
   3                                                      
> (1 2 3)                                                 
application: not a procedure;                             
 expected a procedure that can be applied to arguments    
  given: 1                                                
  arguments...:                                           
   2                                                      
   3                                                      

Some functions, such as cons, accept a fixed number of arguments. Some functions, such as + or list, accept any number of arguments. Some functions accept a range of argument counts; for example substring accepts either two or three arguments. A functions arity is the number of arguments that it accepts.

3.2. Keyword Arguments

Some functions accept keyword arguments in addition to by-position arguments. For that case, an arg can be an arg-keyword arg-expr sequence instead of just a arg-expr:

+[missing] introduces keywords.

(proc-expr arg ...)       
                          
arg = arg-expr            
    | arg-keyword arg-expr

For example,

(go "super.rkt" #:mode 'fast)

calls the function bound to go with "super.rkt" as a by-position argument, and with 'fast as an argument associated with the #:mode keyword. A keyword is implicitly paired with the expression that follows it.

Since a keyword by itself is not an expression, then

(go "super.rkt" #:mode #:fast)

is a syntax error. The #:mode keyword must be followed by an expression to produce an argument value, and #:fast is not an expression.

The order of keyword args determines the order in which arg-exprs are evaluated, but a function accepts keyword arguments independent of their position in the argument list. The above call to go can be equivalently written

(go #:mode 'fast "super.rkt")

+[missing] in [missing] provides more on procedure applications.

3.3. The apply Function

The syntax for function calls supports any number of arguments, but a specific call always specifies a fixed number of arguments. As a result, a function that takes a list of arguments cannot directly apply a function like + to all of the items in a list:

(define (avg lst) ; doesnt work...
  (/ (+ lst) (length lst)))        
> (avg '(1 2 3))                   
+: contract violation              
  expected: number?                
  given: '(1 2 3)                  
(define (avg lst) ; doesnt always work...                 
  (/ (+ (list-ref lst 0) (list-ref lst 1) (list-ref lst 2))
     (length lst)))                                        
> (avg '(1 2 3))                                           
2                                                          
> (avg '(1 2))                                             
list-ref: index too large for list                         
  index: 2                                                 
  in: '(1 2)                                               

The apply function offers a way around this restriction. It takes a function and a list argument, and it applies the function to the values in the list:

(define (avg lst)                
  (/ (apply + lst) (length lst)))
> (avg '(1 2 3))                 
2                                
> (avg '(1 2))                   
3/2                              
> (avg '(1 2 3 4))               
5/2                              

As a convenience, the apply function accepts additional arguments between the function and the list. The additional arguments are effectively consed onto the argument list:

(define (anti-sum lst)
  (apply - 0 lst))    
> (anti-sum '(1 2 3)) 
-6                    

The apply function accepts keyword arguments, too, and it passes them along to the called function:

(apply go #:mode 'fast '("super.rkt"))
(apply go '("super.rkt") #:mode 'fast)

Keywords that are included in applys list argument do not count as keyword arguments for the called function; instead, all arguments in this list are treated as by-position arguments. To pass a list of keyword arguments to a function, use the keyword-apply function, which accepts a function to apply and three lists. The first two lists are in parallel, where the first list contains keywords (sorted by keyword<?), and the second list contains a corresponding argument for each keyword. The third list contains by-position function arguments, as for apply.

(keyword-apply go             
               '(#:mode)      
               '(fast)        
               '("super.rkt"))

4. Functions Procedures: lambda

A lambda expression creates a function. In the simplest case, a lambda expression has the form

(lambda (arg-id ...)
  body ...+)        

A lambda form with n arg-ids accepts n arguments:

> ((lambda (x) x)                                         
   1)                                                     
1                                                         
> ((lambda (x y) (+ x y))                                 
   1 2)                                                   
3                                                         
> ((lambda (x y) (+ x y))                                 
   1)                                                     
#<procedure>: arity mismatch;                             
 the expected number of arguments does not match the given
number                                                    
  expected: 2                                             
  given: 1                                                
  arguments...:                                           
   1                                                      

4.1. Declaring a Rest Argument

A lambda expression can also have the form

(lambda rest-id
  body ...+)   

That is, a lambda expression can have a single rest-id that is not surrounded by parentheses. The resulting function accepts any number of arguments, and the arguments are put into a list bound to rest-id.

Examples:

> ((lambda x x)      
   1 2 3)            
'(1 2 3)             
> ((lambda x x))     
'()                  
> ((lambda x (car x))
   1 2 3)            
1                    

Functions with a rest-id often use apply to call another function that accepts any number of arguments.

+The apply Function describes apply.

Examples:

(define max-mag                       
  (lambda nums                        
    (apply max (map magnitude nums))))
                                      
> (max 1 -2 0)                        
1                                     
> (max-mag 1 -2 0)                    
2                                     

The lambda form also supports required arguments combined with a rest-id:

(lambda (arg-id ...+ . rest-id)
  body ...+)                   

The result of this form is a function that requires at least as many arguments as arg-ids, and also accepts any number of additional arguments.

Examples:

(define max-mag                                           
  (lambda (num . nums)                                    
    (apply max (map magnitude (cons num nums)))))         
                                                          
> (max-mag 1 -2 0)                                        
2                                                         
> (max-mag)                                               
max-mag: arity mismatch;                                  
 the expected number of arguments does not match the given
number                                                    
  expected: at least 1                                    
  given: 0                                                

A rest-id variable is sometimes called a rest argument, because it accepts the “rest” of the function arguments.

4.2. Declaring Optional Arguments

Instead of just an identifier, an argument (other than a rest argument) in a lambda form can be specified with an identifier and a default value:

(lambda gen-formals                
  body ...+)                       
                                   
gen-formals = (arg ...)            
            | rest-id              
            | (arg ...+ . rest-id) 
                                   
arg         = arg-id               
            | [arg-id default-expr]

An argument of the form [arg-id default-expr] is optional. When the argument is not supplied in an application, default-expr produces the default value. The default-expr can refer to any preceding arg-id, and every following arg-id must have a default as well.

Examples:

(define greet                                    
  (lambda (given [surname "Smith"])              
    (string-append "Hello, " given " " surname)))
                                                 
> (greet "John")                                 
"Hello, John Smith"                              
> (greet "John" "Doe")                           
"Hello, John Doe"                                
(define greet                                      
  (lambda (given [surname (if (equal? given "John")
                              "Doe"                
                              "Smith")])           
    (string-append "Hello, " given " " surname)))  
> (greet "John")                                   
"Hello, John Doe"                                  
> (greet "Adam")                                   
"Hello, Adam Smith"                                

4.3. Declaring Keyword Arguments

A lambda form can declare an argument to be passed by keyword, instead of position. Keyword arguments can be mixed with by-position arguments, and default-value expressions can be supplied for either kind of argument:

+Keyword Arguments introduces function calls with keywords.

(lambda gen-formals                            
  body ...+)                                   
                                               
gen-formals = (arg ...)                        
            | rest-id                          
            | (arg ...+ . rest-id)             
                                               
arg         = arg-id                           
            | [arg-id default-expr]            
            | arg-keyword arg-id               
            | arg-keyword [arg-id default-expr]

An argument specified as arg-keyword arg-id is supplied by an application using the same arg-keyword. The position of the keywordidentifier pair in the argument list does not matter for matching with arguments in an application, because it will be matched to an argument value by keyword instead of by position.

(define greet                                    
  (lambda (given #:last surname)                 
    (string-append "Hello, " given " " surname)))
> (greet "John" #:last "Smith")                  
"Hello, John Smith"                              
> (greet #:last "Doe" "John")                    
"Hello, John Doe"                                

An arg-keyword [arg-id default-expr] argument specifies a keyword-based argument with a default value.

Examples:

(define greet                                               
  (lambda (#:hi [hi "Hello"] given #:last [surname "Smith"])
    (string-append hi ", " given " " surname)))             
                                                            
> (greet "John")                                            
"Hello, John Smith"                                         
> (greet "Karl" #:last "Marx")                              
"Hello, Karl Marx"                                          
> (greet "John" #:hi "Howdy")                               
"Howdy, John Smith"                                         
> (greet "Karl" #:last "Marx" #:hi "Guten Tag")             
"Guten Tag, Karl Marx"                                      

The lambda form does not directly support the creation of a function that accepts “rest” keywords. To construct a function that accepts all keyword arguments, use make-keyword-procedure. The function supplied to make-keyword-procedure receives keyword arguments through parallel lists in the first two by-position arguments, and then all by-position arguments from an application as the remaining by-position arguments.

+The apply Function introduces keyword-apply.

Examples:

(define (trace-wrap f)                                 
  (make-keyword-procedure                              
   (lambda (kws kw-args . rest)                        
     (printf "Called with ~s ~s ~s\n" kws kw-args rest)
     (keyword-apply f kws kw-args rest))))             
                                                       
> ((trace-wrap greet) "John" #:hi "Howdy")             
Called with (#:hi) ("Howdy") ("John")                  
"Howdy, John Smith"                                    

+[missing] in [missing] provides more on function expressions.

4.4. Arity-Sensitive Functions: case-lambda

The case-lambda form creates a function that can have completely different behaviors depending on the number of arguments that are supplied. A case-lambda expression has the form

(case-lambda                     
  [formals body ...+]            
  ...)                           
                                 
formals = (arg-id ...)           
        | rest-id                
        | (arg-id ...+ . rest-id)

where each [formals body ...+] is analogous to (lambda formals body ...+). Applying a function produced by case-lambda is like applying a lambda for the first case that matches the number of given arguments.

Examples:

(define greet                                                      
  (case-lambda                                                     
    [(name) (string-append "Hello, " name)]                        
    [(given surname) (string-append "Hello, " given " " surname)]))
                                                                   
> (greet "John")                                                   
"Hello, John"                                                      
> (greet "John" "Smith")                                           
"Hello, John Smith"                                                
> (greet)                                                          
greet: arity mismatch;                                             
 the expected number of arguments does not match the given         
number                                                             
  given: 0                                                         

A case-lambda function cannot directly support optional or keyword arguments.

5. Definitions: define

A basic definition has the form

(define id expr)

in which case id is bound to the result of expr.

Examples:

(define salutation (list-ref '("Hi" "Hello") (random 2)))
                                                         
> salutation                                             
"Hello"                                                  

5.1. Function Shorthand

The define form also supports a shorthand for function definitions:

(define (id arg ...) body ...+)

which is a shorthand for

(define id (lambda (arg ...) body ...+))

Examples:

(define (greet name)                   
  (string-append salutation ", " name))
                                       
> (greet "John")                       
"Hello, John"                          
(define (greet first [surname "Smith"] #:hi [hi salutation])
  (string-append hi ", " first " " surname))                
> (greet "John")                                            
"Hello, John Smith"                                         
> (greet "John" #:hi "Hey")                                 
"Hey, John Smith"                                           
> (greet "John" "Doe")                                      
"Hello, John Doe"                                           

The function shorthand via define also supports a rest argument i.e., a final argument to collect extra arguments in a list:

(define (id arg ... . rest-id) body ...+)

which is a shorthand

(define id (lambda (arg ... . rest-id) body ...+))

Examples:

(define (avg . l)            
  (/ (apply + l) (length l)))
                             
> (avg 1 2 3)                
2                            

5.2. Curried Function Shorthand

Consider the following make-add-suffix function that takes a string and returns another function that takes a string:

(define make-add-suffix                
  (lambda (s2)                         
    (lambda (s) (string-append s s2))))

Although its not common, result of make-add-suffix could be called directly, like this:

> ((make-add-suffix "!") "hello")
"hello!"                         

In a sense, make-add-suffix is a function takes two arguments, but it takes them one at a time. A function that takes some of its arguments and returns a function to consume more is sometimes called a curried function.

Using the function-shorthand form of define, make-add-suffix can be written equivalently as

(define (make-add-suffix s2)        
  (lambda (s) (string-append s s2)))

This shorthand reflects the shape of the function call (make-add-suffix "!"). The define form further supports a shorthand for defining curried functions that reflects nested function calls:

(define ((make-add-suffix s2) s) 
  (string-append s s2))          
> ((make-add-suffix "!") "hello")
"hello!"                         
(define louder (make-add-suffix "!"))   
(define less-sure (make-add-suffix "?"))
> (less-sure "really")                  
"really?"                               
> (louder "really")                     
"really!"                               

The full syntax of the function shorthand for define is as follows:

(define (head args) body ...+)
                              
head = id                     
     | (head args)            
                              
args = arg ...                
     | arg ... . rest-id      

The expansion of this shorthand has one nested lambda form for each head in the definition, where the innermost head corresponds to the outermost lambda.

5.3. Multiple Values and define-values

A Racket expression normally produces a single result, but some expressions can produce multiple results. For example, quotient and remainder each produce a single value, but quotient/remainder produces the same two values at once:

> (quotient 13 3)          
4                          
> (remainder 13 3)         
1                          
> (quotient/remainder 13 3)
4                          
1                          

As shown above, the REPL prints each result value on its own line.

Multiple-valued functions can be implemented in terms of the values function, which takes any number of values and returns them as the results:

> (values 1 2 3)
1               
2               
3               
(define (split-name name)                             
  (let ([parts (regexp-split " " name)])              
    (if (= (length parts) 2)                          
        (values (list-ref parts 0) (list-ref parts 1))
        (error "not a <first> <last> name"))))        
> (split-name "Adam Smith")                           
"Adam"                                                
"Smith"                                               

The define-values form binds multiple identifiers at once to multiple results produced from a single expression:

(define-values (id ...) expr)

The number of results produced by the expr must match the number of ids.

Examples:

(define-values (given surname) (split-name "Adam Smith"))
                                                         
> given                                                  
"Adam"                                                   
> surname                                                
"Smith"                                                  

A define form that is not a function shorthand is equivalent to a define-values form with a single id.

+[missing] in [missing] provides more on definitions.

5.4. Internal Definitions

When the grammar for a syntactic form specifies body, then the corresponding form can be either a definition or an expression. A definition as a body is an internal definition.

Expressions and internal definitions in a body sequence can be mixed, as long as the last body is an expression.

For example, the syntax of lambda is

(lambda gen-formals
  body ...+)       

so the following are valid instances of the grammar:

(lambda (f)                ; no definitions 
  (printf "running\n")                      
  (f 0))                                    
                                            
(lambda (f)                ; one definition 
  (define (log-it what)                     
    (printf "~a\n" what))                   
  (log-it "running")                        
  (f 0)                                     
  (log-it "done"))                          
                                            
(lambda (f n)              ; two definitions
  (define (call n)                          
    (if (zero? n)                           
        (log-it "done")                     
        (begin                              
          (log-it "running")                
          (f n)                             
          (call (- n 1)))))                 
  (define (log-it what)                     
    (printf "~a\n" what))                   
  (call n))                                 

Internal definitions in a particular body sequence are mutually recursive; that is, any definition can refer to any other definition—as long as the reference isnt actually evaluated before its definition takes place. If a definition is referenced too early, an error occurs.

Examples:

(define (weird)                  
  (define x x)                   
  x)                             
                                 
> (weird)                        
x: undefined;                    
 cannot use before initialization

A sequence of internal definitions using just define is easily translated to an equivalent letrec form (as introduced in the next section). However, other definition forms can appear as a body, including define-values, struct see \[missing\] or define-syntax see \[missing\].

+[missing] in [missing] documents the fine points of internal definitions.

6. Local Binding

Although internal defines can be used for local binding, Racket provides three forms that give the programmer more control over bindings: let, let*, and letrec.

6.1. Parallel Binding: let

+[missing] in [missing] also documents let.

A let form binds a set of identifiers, each to the result of some expression, for use in the let body:

(let ([id expr] ...) body ...+)

The ids are bound “in parallel.” That is, no id is bound in the right-hand side expr for any id, but all are available in the body. The ids must be different from each other.

Examples:

> (let ([me "Bob"])                      
    me)                                  
"Bob"                                    
> (let ([me "Bob"]                       
        [myself "Robert"]                
        [I "Bobby"])                     
    (list me myself I))                  
'("Bob" "Robert" "Bobby")                
> (let ([me "Bob"]                       
        [me "Robert"])                   
    me)                                  
eval:3:0: let: duplicate identifier      
  at: me                                 
  in: (let ((me "Bob") (me "Robert")) me)

The fact that an ids expr does not see its own binding is often useful for wrappers that must refer back to the old value:

> (let ([+ (lambda (x y)                     
             (if (string? x)                 
                 (string-append x y)         
                 (+ x y)))]) ; use original +
    (list (+ 1 2)                            
          (+ "see" "saw")))                  
'(3 "seesaw")                                

Occasionally, the parallel nature of let bindings is convenient for swapping or rearranging a set of bindings:

> (let ([me "Tarzan"]
        [you "Jane"])
    (let ([me you]   
          [you me])  
      (list me you)))
'("Jane" "Tarzan")   

The characterization of let bindings as “parallel” is not meant to imply concurrent evaluation. The exprs are evaluated in order, even though the bindings are delayed until all exprs are evaluated.

6.2. Sequential Binding: let*

+[missing] in [missing] also documents let*.

The syntax of let* is the same as let:

(let* ([id expr] ...) body ...+)

The difference is that each id is available for use in later exprs, as well as in the body. Furthermore, the ids need not be distinct, and the most recent binding is the visible one.

Examples:

> (let* ([x (list "Burroughs")]                                   
         [y (cons "Rice" x)]                                      
         [z (cons "Edgar" y)])                                    
    (list x y z))                                                 
'(("Burroughs") ("Rice" "Burroughs") ("Edgar" "Rice" "Burroughs"))
> (let* ([name (list "Burroughs")]                                
         [name (cons "Rice" name)]                                
         [name (cons "Edgar" name)])                              
    name)                                                         
'("Edgar" "Rice" "Burroughs")                                     

In other words, a let* form is equivalent to nested let forms, each with a single binding:

> (let ([name (list "Burroughs")])     
    (let ([name (cons "Rice" name)])   
      (let ([name (cons "Edgar" name)])
        name)))                        
'("Edgar" "Rice" "Burroughs")          

6.3. Recursive Binding: letrec

+[missing] in [missing] also documents letrec.

The syntax of letrec is also the same as let:

(letrec ([id expr] ...) body ...+)

While let makes its bindings available only in the bodys, and let* makes its bindings available to any later binding expr, letrec makes its bindings available to all other exprs—even earlier ones. In other words, letrec bindings are recursive.

The exprs in a letrec form are most often lambda forms for recursive and mutually recursive functions:

> (letrec ([swing                               
            (lambda (t)                         
              (if (eq? (car t) 'tarzan)         
                  (cons 'vine                   
                        (cons 'tarzan (cddr t)))
                  (cons (car t)                 
                        (swing (cdr t)))))])    
    (swing '(vine tarzan vine vine)))           
'(vine vine tarzan vine)                        
> (letrec ([tarzan-near-top-of-tree?                                     
            (lambda (name path depth)                                    
              (or (equal? name "tarzan")                                 
                  (and (directory-exists? path)                          
                       (tarzan-in-directory? path depth))))]             
           [tarzan-in-directory?                                         
            (lambda (dir depth)                                          
              (cond                                                      
                [(zero? depth) #f]                                       
                [else                                                    
                 (ormap                                                  
                  (λ (elem)                                              
                    (tarzan-near-top-of-tree? (path-element->string elem)
                                              (build-path dir elem)      
                                              (- depth 1)))              
                  (directory-list dir))]))])                             
    (tarzan-near-top-of-tree? "tmp"                                      
                              (find-system-path 'temp-dir)               
                              4))                                        
#f                                                                       

While the exprs of a letrec form are typically lambda expressions, they can be any expression. The expressions are evaluated in order, and after each value is obtained, it is immediately associated with its corresponding id. If an id is referenced before its value is ready, an error is raised, just as for internal definitions.

> (letrec ([quicksand quicksand])
    quicksand)                   
quicksand: undefined;            
 cannot use before initialization

6.4. Named let

A named let is an iteration and recursion form. It uses the same syntactic keyword let as for local binding, but an identifier after the let instead of an immediate open parenthesis triggers a different parsing.

(let proc-id ([arg-id init-expr] ...)
  body ...+)                         

A named let form is equivalent to

(letrec ([proc-id (lambda (arg-id ...)
                     body ...+)])     
  (proc-id init-expr ...))            

That is, a named let binds a function identifier that is visible only in the functions body, and it implicitly calls the function with the values of some initial expressions.

Examples:

(define (duplicate pos lst)                             
  (let dup ([i 0]                                       
            [lst lst])                                  
   (cond                                                
    [(= i pos) (cons (car lst) lst)]                    
    [else (cons (car lst) (dup (+ i 1) (cdr lst)))])))  
                                                        
> (duplicate 1 (list "apple" "cheese burger!" "banana"))
'("apple" "cheese burger!" "cheese burger!" "banana")   

6.5. Multiple Values: let-values, let*-values, letrec-values

+[missing] in [missing] also documents multiple-value binding forms.

In the same way that define-values binds multiple results in a definition see Multiple Values and `define-values`, let-values, let*-values, and letrec-values bind multiple results locally.

(let-values ([(id ...) expr] ...)
  body ...+)                     
(let*-values ([(id ...) expr] ...)
  body ...+)                      
(letrec-values ([(id ...) expr] ...)
  body ...+)                        

Each expr must produce as many values as corresponding ids. The binding rules are the same for the forms without -values forms: the ids of let-values are bound only in the bodys, the ids of let*-valuess are bound in exprs of later clauses, and the ids of letrec-values are bound for all exprs.

Example:

> (let-values ([(q r) (quotient/remainder 14 3)])
    (list q r))                                  
'(4 2)                                           

7. Conditionals

Most functions used for branching, such as < and string?, produce either #t or #f. Rackets branching forms, however, treat any value other than #f as true. We say a true value to mean any value other than #f.

This convention for “true value” meshes well with protocols where #f can serve as failure or to indicate that an optional value is not supplied. (Beware of overusing this trick, and remember that an exception is usually a better mechanism to report failure.)

For example, the member function serves double duty; it can be used to find the tail of a list that starts with a particular item, or it can be used to simply check whether an item is present in a list:

> (member "Groucho" '("Harpo" "Zeppo"))              
#f                                                   
> (member "Groucho" '("Harpo" "Groucho" "Zeppo"))    
'("Groucho" "Zeppo")                                 
> (if (member "Groucho" '("Harpo" "Zeppo"))          
      'yep                                           
      'nope)                                         
'nope                                                
> (if (member "Groucho" '("Harpo" "Groucho" "Zeppo"))
      'yep                                           
      'nope)                                         
'yep                                                 

7.1. Simple Branching: if

+[missing] in [missing] also documents if.

In an if form,

(if test-expr then-expr else-expr)

the test-expr is always evaluated. If it produces any value other than #f, then then-expr is evaluated. Otherwise, else-expr is evaluated.

An if form must have both a then-expr and an else-expr; the latter is not optional. To perform or skip side-effects based on a test-expr, use when or unless, which we describe later in Sequencing.

7.2. Combining Tests: and and or

+[missing] in [missing] also documents and and or.

Rackets and and or are syntactic forms, rather than functions. Unlike a function, the and and or forms can skip evaluation of later expressions if an earlier one determines the answer.

(and expr ...)

An and form produces #f if any of its exprs produces #f. Otherwise, it produces the value of its last expr. As a special case, (and) produces #t.

(or expr ...)

The or form produces #f if all of its exprs produce #f. Otherwise, it produces the first non-#f value from its exprs. As a special case, (or) produces #f.

Examples:

> (define (got-milk? lst)                                    
    (and (not (null? lst))                                   
         (or (eq? 'milk (car lst))                           
             (got-milk? (cdr lst))))) ; recurs only if needed
> (got-milk? '(apple banana))                                
#f                                                           
> (got-milk? '(apple milk banana))                           
#t                                                           

If evaluation reaches the last expr of an and or or form, then the exprs value directly determines the and or or result. Therefore, the last expr is in tail position, which means that the above got-milk? function runs in constant space.

+[missing] introduces tail calls and tail positions.

7.3. Chaining Tests: cond

The cond form chains a series of tests to select a result expression. To a first approximation, the syntax of cond is as follows:

+[missing] in [missing] also documents cond.

(cond [test-expr body ...+]
      ...)                 

Each test-expr is evaluated in order. If it produces #f, the corresponding bodys are ignored, and evaluation proceeds to the next test-expr. As soon as a test-expr produces a true value, its bodys are evaluated to produce the result for the cond form, and no further test-exprs are evaluated.

The last test-expr in a cond can be replaced by else. In terms of evaluation, else serves as a synonym for #t, but it clarifies that the last clause is meant to catch all remaining cases. If else is not used, then it is possible that no test-exprs produce a true value; in that case, the result of the cond expression is #<void>.

Examples:

> (cond                       
   [(= 2 3) (error "wrong!")] 
   [(= 2 2) 'ok])             
'ok                           
> (cond                       
   [(= 2 3) (error "wrong!")])
> (cond                       
   [(= 2 3) (error "wrong!")] 
   [else 'ok])                
'ok                           
(define (got-milk? lst)           
  (cond                           
    [(null? lst) #f]              
    [(eq? 'milk (car lst)) #t]    
    [else (got-milk? (cdr lst))]))
> (got-milk? '(apple banana))     
#f                                
> (got-milk? '(apple milk banana))
#t                                

The full syntax of cond includes two more kinds of clauses:

(cond cond-clause ...)                  
                                        
cond-clause = [test-expr then-body ...+]
            | [else then-body ...+]     
            | [test-expr => proc-expr]  
            | [test-expr]               

The => variant captures the true result of its test-expr and passes it to the result of the proc-expr, which must be a function of one argument.

Examples:

> (define (after-groucho lst)                 
    (cond                                     
      [(member "Groucho" lst) => cdr]         
      [else (error "not there")]))            
> (after-groucho '("Harpo" "Groucho" "Zeppo"))
'("Zeppo")                                    
> (after-groucho '("Harpo" "Zeppo"))          
not there                                     

A clause that includes only a test-expr is rarely used. It captures the true result of the test-expr, and simply returns the result for the whole cond expression.

8. Sequencing

Racket programmers prefer to write programs with as few side-effects as possible, since purely functional code is more easily tested and composed into larger programs. Interaction with the external environment, however, requires sequencing, such as when writing to a display, opening a graphical window, or manipulating a file on disk.

8.1. Effects Before: begin

+[missing] in [missing] also documents begin.

A begin expression sequences expressions:

(begin expr ...+)

The exprs are evaluated in order, and the result of all but the last expr is ignored. The result from the last expr is the result of the begin form, and it is in tail position with respect to the begin form.

Examples:

(define (print-triangle height)           
  (if (zero? height)                      
      (void)                              
      (begin                              
        (display (make-string height #\*))
        (newline)                         
        (print-triangle (sub1 height))))) 
                                          
> (print-triangle 4)                      
****                                      
***                                       
**                                        
*                                         

Many forms, such as lambda or cond support a sequence of expressions even without a begin. Such positions are sometimes said to have an implicit begin.

Examples:

(define (print-triangle height)        
  (cond                                
    [(positive? height)                
     (display (make-string height #\*))
     (newline)                         
     (print-triangle (sub1 height))])) 
                                       
> (print-triangle 4)                   
****                                   
***                                    
**                                     
*                                      

The begin form is special at the top level, at module level, or as a body after only internal definitions. In those positions, instead of forming an expression, the content of begin is spliced into the surrounding context.

Example:

> (let ([curly 0])             
    (begin                     
      (define moe (+ 1 curly)) 
      (define larry (+ 1 moe)))
    (list larry curly moe))    
'(2 0 1)                       

This splicing behavior is mainly useful for macros, as we discuss later in [missing].

8.2. Effects After: begin0

+[missing] in [missing] also documents begin0.

A begin0 expression has the same syntax as a begin expression:

(begin0 expr ...+)

The difference is that begin0 returns the result of the first expr, instead of the result of the last expr. The begin0 form is useful for implementing side-effects that happen after a computation, especially in the case where the computation produces an unknown number of results.

Examples:

(define (log-times thunk)                                  
  (printf "Start: ~s\n" (current-inexact-milliseconds))    
  (begin0                                                  
    (thunk)                                                
    (printf "End..: ~s\n" (current-inexact-milliseconds))))
                                                           
> (log-times (lambda () (sleep 0.1) 0))                    
Start: 1548117365804.718                                   
End..: 1548117365906.647                                   
0                                                          
> (log-times (lambda () (values 1 2)))                     
Start: 1548117365907.178                                   
End..: 1548117365907.202                                   
1                                                          
2                                                          

8.3. Effects If...: when and unless

+[missing] in [missing] also documents when and unless.

The when form combines an if-style conditional with sequencing for the “then” clause and no “else” clause:

(when test-expr then-body ...+)

If test-expr produces a true value, then all of the then-bodys are evaluated. The result of the last then-body is the result of the when form. Otherwise, no then-bodys are evaluated and the result is #<void>.

The unless form is similar:

(unless test-expr then-body ...+)

The difference is that the test-expr result is inverted: the then-bodys are evaluated only if the test-expr result is #f.

Examples:

(define (enumerate lst)               
  (if (null? (cdr lst))               
      (printf "~a.\n" (car lst))      
      (begin                          
        (printf "~a, " (car lst))     
        (when (null? (cdr (cdr lst))) 
          (printf "and "))            
        (enumerate (cdr lst)))))      
                                      
> (enumerate '("Larry" "Curly" "Moe"))
Larry, Curly, and Moe.                
(define (print-triangle height)       
  (unless (zero? height)              
    (display (make-string height #\*))
    (newline)                         
    (print-triangle (sub1 height))))  
> (print-triangle 4)                  
****                                  
***                                   
**                                    
*                                     

9. Assignment: set!

+[missing] in [missing] also documents set!.

Assign to a variable using set!:

(set! id expr)

A set! expression evaluates expr and changes id (which must be bound in the enclosing environment) to the resulting value. The result of the set! expression itself is #<void>.

Examples:

(define greeted null)               
                                    
(define (greet name)                
  (set! greeted (cons name greeted))
  (string-append "Hello, " name))   
                                    
> (greet "Athos")                   
"Hello, Athos"                      
> (greet "Porthos")                 
"Hello, Porthos"                    
> (greet "Aramis")                  
"Hello, Aramis"                     
> greeted                           
'("Aramis" "Porthos" "Athos")       
(define (make-running-total)      
  (let ([n 0])                    
    (lambda ()                    
      (set! n (+ n 1))            
      n)))                        
(define win (make-running-total)) 
(define lose (make-running-total))
> (win)                           
1                                 
> (win)                           
2                                 
> (lose)                          
1                                 
> (win)                           
3                                 

9.1. Guidelines for Using Assignment

Although using set! is sometimes appropriate, Racket style generally discourages the use of set!. The following guidelines may help explain when using set! is appropriate.

  • As in any modern language, assigning to a shared identifier is no substitute for passing an argument to a procedure or getting its result.

    Really awful example:

    (define name "unknown")                        
    (define result "unknown")                      
    (define (greet)                                
      (set! result (string-append "Hello, " name)))
    
    > (set! name "John")                           
    > (greet)                                      
    > result                                       
    "Hello, John"                                  
    

    Ok example:

    (define (greet name)             
      (string-append "Hello, " name))
    
    > (greet "John")                 
    "Hello, John"                    
    > (greet "Anna")                 
    "Hello, Anna"                    
    
  • A sequence of assignments to a local variable is far inferior to nested bindings.

    Bad example:

(let ([tree 0])
(set! tree (list tree 1 tree))
(set! tree (list tree 2 tree))
(set! tree (list tree 3 tree))
tree)
'(((0 1 0) 2 (0 1 0)) 3 ((0 1 0) 2 (0 1 0)))


  Ok example:

  ```racket
> (let* ([tree 0]                           
           [tree (list tree 1 tree)]          
           [tree (list tree 2 tree)]          
           [tree (list tree 3 tree)])         
      tree)                                   
  '(((0 1 0) 2 (0 1 0)) 3 ((0 1 0) 2 (0 1 0)))
  • Using assignment to accumulate results from an iteration is bad style. Accumulating through a loop argument is better.

    Somewhat bad example:

    (define (sum lst)                          
      (let ([s 0])                             
        (for-each (lambda (i) (set! s (+ i s)))
                  lst)                         
        s))                                    
    
    > (sum '(1 2 3))                           
    6                                          
    

    Ok example:

    (define (sum lst)                          
      (let loop ([lst lst] [s 0])              
        (if (null? lst)                        
            s                                  
            (loop (cdr lst) (+ s (car lst))))))
    
    > (sum '(1 2 3))                           
    6                                          
    

    Better use an existing function example:

    (define (sum lst)
      (apply + lst)) 
    
    > (sum '(1 2 3)) 
    6                
    

    Good a general approach example:

    (define (sum lst)              
      (for/fold ([s 0])            
                ([i (in-list lst)])
        (+ s i)))                  
    
    > (sum '(1 2 3))               
    6                              
    
  • For cases where stateful objects are necessary or appropriate, then implementing the objects state with set! is fine.

    Ok example:

    (define next-number!   
      (let ([n 0])         
        (lambda ()         
          (set! n (add1 n))
          n)))             
    
    > (next-number!)       
    1                      
    > (next-number!)       
    2                      
    > (next-number!)       
    3                      
    

All else being equal, a program that uses no assignments or mutation is always preferable to one that uses assignments or mutation. While side effects are to be avoided, however, they should be used if the resulting code is significantly more readable or if it implements a significantly better algorithm.

The use of mutable values, such as vectors and hash tables, raises fewer suspicions about the style of a program than using set! directly. Nevertheless, simply replacing set!s in a program with vector-set!s obviously does not improve the style of the program.

9.2. Multiple Values: set!-values

+[missing] in [missing] also documents set!-values.

The set!-values form assigns to multiple variables at once, given an expression that produces an appropriate number of values:

(set!-values (id ...) expr)

This form is equivalent to using let-values to receive multiple results from expr, and then assigning the results individually to the ids using set!.

Examples:

(define game                                
  (let ([w 0]                               
        [l 0])                              
    (lambda (win?)                          
      (if win?                              
          (set! w (+ w 1))                  
          (set! l (+ l 1)))                 
      (begin0                               
        (values w l)                        
        ; swap sides...                     
        (set!-values (w l) (values l w))))))
                                            
> (game #t)                                 
1                                           
0                                           
> (game #t)                                 
1                                           
1                                           
> (game #f)                                 
1                                           
2                                           

10. Quoting: quote and '

+[missing] in [missing] also documents quote.

The quote form produces a constant:

(quote datum)

The syntax of a datum is technically specified as anything that the read function parses as a single element. The value of the quote form is the same value that read would produce given datum.

The datum can be a symbol, a boolean, a number, a (character or byte) string, a character, a keyword, an empty list, a pair or list containing more such values, a vector containing more such values, a hash table containing more such values, or a box containing another such value.

Examples:

> (quote apple)                       
'apple                                
> (quote #t)                          
#t                                    
> (quote 42)                          
42                                    
> (quote "hello")                     
"hello"                               
> (quote ())                          
'()                                   
> (quote ((1 2 3) #("z" x) . the-end))
'((1 2 3) #("z" x) . the-end)         
> (quote (1 2 . (3)))                 
'(1 2 3)                              

As the last example above shows, the datum does not have to match the normalized printed form of a value. A datum cannot be a printed representation that starts with #<, so it cannot be #<void>, #<undefined>, or a procedure.

The quote form is rarely used for a datum that is a boolean, number, or string by itself, since the printed forms of those values can already be used as constants. The quote form is more typically used for symbols and lists, which have other meanings (identifiers, function calls, etc.) when not quoted.

An expression

'datum

is a shorthand for

(quote datum)

and this shorthand is almost always used instead of quote. The shorthand applies even within the datum, so it can produce a list containing quote.

+[missing] in [missing] provides more on the ' shorthand.

Examples:

> 'apple                  
'apple                    
> '"hello"                
"hello"                   
> '(1 2 3)                
'(1 2 3)                  
> (display '(you can 'me))
(you can (quote me))      

11. Quasiquoting: quasiquote and

+[missing] in [missing] also documents quasiquote.

The quasiquote form is similar to quote:

(quasiquote datum)

However, for each (unquote expr) that appears within the datum, the expr is evaluated to produce a value that takes the place of the unquote sub-form.

Example:

> (quasiquote (1 2 (unquote (+ 1 2)) (unquote (- 5 1))))
'(1 2 3 4)                                              

This form can be used to write functions that build lists according to certain patterns.

Examples:

> (define (deep n)                                           
    (cond                                                    
      [(zero? n) 0]                                          
      [else                                                  
       (quasiquote ((unquote n) (unquote (deep (- n 1)))))]))
> (deep 8)                                                   
'(8 (7 (6 (5 (4 (3 (2 (1 0))))))))                           

Or even to cheaply construct expressions programmatically. (Of course, 9 times out of 10, you should be using a macro to do this (the 10th time being when youre working through a textbook like PLAI).)

Examples:

> (define (build-exp n)                                       
    (add-lets n (make-sum n)))                                
> (define (add-lets n body)                                   
    (cond                                                     
      [(zero? n) body]                                        
      [else                                                   
       (quasiquote                                            
        (let ([(unquote (n->var n)) (unquote n)])             
          (unquote (add-lets (- n 1) body))))]))              
> (define (make-sum n)                                        
    (cond                                                     
      [(= n 1) (n->var 1)]                                    
      [else                                                   
       (quasiquote (+ (unquote (n->var n))                    
                      (unquote (make-sum (- n 1)))))]))       
> (define (n->var n) (string->symbol (format "x~a" n)))       
> (build-exp 3)                                               
'(let ((x3 3)) (let ((x2 2)) (let ((x1 1)) (+ x3 (+ x2 x1)))))

The unquote-splicing form is similar to unquote, but its expr must produce a list, and the unquote-splicing form must appear in a context that produces either a list or a vector. As the name suggests, the resulting list is spliced into the context of its use.

Example:

> (quasiquote (1 2 (unquote-splicing (list (+ 1 2) (- 5 1))) 5))
'(1 2 3 4 5)                                                    

Using splicing we can revise the construction of our example expressions above to have just a single let expression and a single + expression.

Examples:

> (define (build-exp n)                                          
    (add-lets                                                    
     n                                                           
     (quasiquote (+ (unquote-splicing                            
                     (build-list                                 
                      n                                          
                      (λ (x) (n->var (+ x 1)))))))))             
> (define (add-lets n body)                                      
    (quasiquote                                                  
     (let (unquote                                               
           (build-list                                           
            n                                                    
            (λ (n)                                               
              (quasiquote                                        
               [(unquote (n->var (+ n 1))) (unquote (+ n 1))]))))
       (unquote body))))                                         
> (define (n->var n) (string->symbol (format "x~a" n)))          
> (build-exp 3)                                                  
'(let ((x1 1) (x2 2) (x3 3)) (+ x1 x2 x3))                       

If a quasiquote form appears within an enclosing quasiquote form, then the inner quasiquote effectively cancels one layer of unquote and unquote-splicing forms, so that a second unquote or unquote-splicing is needed.

Examples:

> (quasiquote (1 2 (quasiquote (unquote (+ 1 2)))))                            
'(1 2 (quasiquote (unquote (+ 1 2))))                                          
> (quasiquote (1 2 (quasiquote (unquote (unquote (+ 1 2))))))                  
'(1 2 (quasiquote (unquote 3)))                                                
>                                                                              
(quasiquote (1 2 (quasiquote ((unquote (+ 1 2)) (unquote (unquote (- 5 1)))))))
'(1 2 (quasiquote ((unquote (+ 1 2)) (unquote 4))))                            

The evaluations above will not actually print as shown. Instead, the shorthand form of quasiquote and unquote will be used: \(i.e., a backquote\) and,` i.e., a comma. The same shorthands can be used in expressions:

Example:

> `(1 2 `(,(+ 1 2) ,,(- 5 1)))
'(1 2 `(,(+ 1 2) ,4))         

The shorthand form of unquote-splicing is ,@:

Example:

> `(1 2 ,@(list (+ 1 2) (- 5 1)))
'(1 2 3 4)                       

12. Simple Dispatch: case

The case form dispatches to a clause by matching the result of an expression to the values for the clause:

(case expr                
  [(datum ...+) body ...+]
  ...)                    

Each datum will be compared to the result of expr using equal?, and then the corresponding bodys are evaluated. The case form can dispatch to the correct clause in O_log N___ time for N datums.

Multiple datums can be supplied for each clause, and the corresponding bodys are evaluated if any of the datums match.

Example:

> (let ([v (random 6)])
    (printf "~a\n" v)  
    (case v            
      [(0) 'zero]      
      [(1) 'one]       
      [(2) 'two]       
      [(3 4 5) 'many]))
2                      
'two                   

The last clause of a case form can use else, just like cond:

Example:

> (case (random 6)
    [(0) 'zero]   
    [(1) 'one]    
    [(2) 'two]    
    [else 'many]) 
'two              

For more general pattern matching (but without the dispatch-time guarantee), use match, which is introduced in [missing].

13. Dynamic Binding: parameterize

+[missing] in [missing] also documents parameterize.

The parameterize form associates a new value with a parameter during the evaluation of body expressions:

(parameterize ([parameter-expr value-expr] ...)
  body ...+)                                   

The term “parameter” is sometimes used to refer to the arguments of a function, but “parameter” in Racket has the more specific meaning described here.

For example, the error-print-width parameter controls how many characters of a value are printed in an error message:

> (parameterize ([error-print-width 5]) 
    (car (expt 10 1024)))               
car: contract violation                 
  expected: pair?                       
  given: 10...                          
> (parameterize ([error-print-width 10])
    (car (expt 10 1024)))               
car: contract violation                 
  expected: pair?                       
  given: 1000000...                     

More generally, parameters implement a kind of dynamic binding. The make-parameter function takes any value and returns a new parameter that is initialized to the given value. Applying the parameter as a function returns its current value:

> (define location (make-parameter "here"))
> (location)                               
"here"                                     

In a parameterize form, each parameter-expr must produce a parameter. During the evaluation of the bodys, each specified parameter is given the result of the corresponding value-expr. When control leaves the parameterize form—either through a normal return, an exception, or some other escape—the parameter reverts to its earlier value:

> (parameterize ([location "there"])               
    (location))                                    
"there"                                            
> (location)                                       
"here"                                             
> (parameterize ([location "in a house"])          
    (list (location)                               
          (parameterize ([location "with a mouse"])
            (location))                            
          (location)))                             
'("in a house" "with a mouse" "in a house")        
> (parameterize ([location "in a box"])            
    (car (location)))                              
car: contract violation                            
  expected: pair?                                  
  given: "in a box"                                
> (location)                                       
"here"                                             

The parameterize form is not a binding form like let; each use of location above refers directly to the original definition. A parameterize form adjusts the value of a parameter during the whole time that the parameterize body is evaluated, even for uses of the parameter that are textually outside of the parameterize body:

> (define (would-you-could-you?)            
    (and (not (equal? (location) "here"))   
         (not (equal? (location) "there"))))
> (would-you-could-you?)                    
#f                                          
> (parameterize ([location "on a bus"])     
    (would-you-could-you?))                 
#t                                          

If a use of a parameter is textually inside the body of a parameterize but not evaluated before the parameterize form produces a value, then the use does not see the value installed by the parameterize form:

> (let ([get (parameterize ([location "with a fox"])
               (lambda () (location)))])            
    (get))                                          
"here"                                              

The current binding of a parameter can be adjusted imperatively by calling the parameter as a function with a value. If a parameterize has adjusted the value of the parameter, then directly applying the parameter procedure affects only the value associated with the active parameterize:

> (define (try-again! where)             
    (location where))                    
> (location)                             
"here"                                   
> (parameterize ([location "on a train"])
    (list (location)                     
          (begin (try-again! "in a boat")
                 (location))))           
'("on a train" "in a boat")              
> (location)                             
"here"                                   

Using parameterize is generally preferable to updating a parameter value imperatively—for much the same reasons that binding a fresh variable with let is preferable to using set! (see Assignment: set!).

It may seem that variables and set! can solve many of the same problems that parameters solve. For example, lokation could be defined as a string, and set! could be used to adjust its value:

> (define lokation "here")                
> (define (would-ya-could-ya?)            
    (and (not (equal? lokation "here"))   
         (not (equal? lokation "there"))))
> (set! lokation "on a bus")              
> (would-ya-could-ya?)                    
#t                                        

Parameters, however, offer several crucial advantages over set!:

  • The parameterize form helps automatically reset the value of a parameter when control escapes due to an exception. Adding exception handlers and other forms to rewind a set! is relatively tedious.

  • Parameters work nicely with tail calls see \[missing\]. The last body in a parameterize form is in tail position with respect to the parameterize form.

  • Parameters work properly with threads see \[missing\]. The parameterize form adjusts the value of a parameter only for evaluation in the current thread, which avoids race conditions with other threads.