Basic Syntax Extensions

2.1  Evaluation Order

In an application expression, the procedure expression and the argument expressions are always evaluated left-to-right. Similarly, expressions for let and letrec bindings are evaluated in sequence from left to right.

2.2  Multiple Return Values

MzScheme supports the R5RS values and call-with-values procedure, and also provides binding forms for multiple-value expressions, discussed in section 2.8.

Multiple return values are legal in MzScheme whenever the return value of an expression is ignored. For example, all but the last expression in a begin form can legally return multiple values in any context. If a built-in procedure takes a procedure argument, and the built-in procedure does not inspect the result of the supplied procedure, then the supplied procedure can return multiple values. For example, the procedure supplied to for-each can return any number of values, but the procedure supplied to map must return a single value.

When the number of values returned by an expression does not match the number of values expected by the expression's context, the exn:fail:contract:arity exception is raised (at run time).

Examples:

(- (values 1)) ; => -1
(- (values 1 2)) ; => exn:fail:contract:arity, returned 2 values to single-value context
(- (values)) ; => exn:fail:contract:arity, returned 0 values to single-value context
(call-with-values 
  (lambda () (values 1 2)) 
  (lambda (x y) y)) ; => 2
(call-with-values 
  (lambda () (values 1 2)) 
  (lambda z z)) ; => (1 2) 
(call-with-values 
  (lambda () (let/cc k (k 3 4))) 
  (lambda (x y) y)) ; => 4
(call-with-values 
  (lambda () (values 'hello 1 2 3 4)) 
  (lambda (s . l) 
    (format "~s = ~s" s l))) ; => "hello = (1 2 3 4)"

2.3  Cond and Case

The else and => identifiers in a cond or case statement are handled specially only when they are not lexically bound or module-bound:

(cond [1 => add1]) ; => 2
(let ([=> 5]) (cond [1 => add1])) ; => #<primitive:add1>

2.4  When and Unless

The when and unless forms conditionally evaluate a single body of expressions:

The result of a when or unless expression is the result of the last body expression if the body is evaluated, or void (see section 3.1) if the body is not evaluated.

2.5  And and Or

In an and or or expression, the last test expression can return multiple values (see section 2.2). If the last expression is evaluated and it returns multiple values, then the result of the entire and or or expression is the multiple values. Other sub-expressions in an and or or expression must return a single value.

2.6  Sequences

The begin0 form is like begin, but the value of the first expression in the form is returned instead of the value of the last expression:

(let ([x 4])
  (begin0 x (set! x 9) (display x))) ; => displays 9 then returns 4

The first sub-expression in a begin0 expression is in tail position if and only if it is the only sub-expression.

2.7  Quote and Quasiquote

The quote form never allocates, so that the result of multiple evaluations of a single quote expression are always eq?. Nevertheless, a quoted cons cell, vector, or list is mutable; mutations to the result of a quote application are visible to future evaluations of the quote expression.

The quasiquote form allocates only as many fresh cons cells, vectors, and boxes as are needed without analyzing unquote and unquote-splicing expressions. For example, in

`(,1 2 3)

a single reader-allocated tail '(2 3) is used for every evaluation of the quasiquote expression.

The standard Scheme quasiquote has been extended so that unquote and unquote-splicing work within immediate boxes:

`#&(,(- 2 1) ,@(list 2 3)) ; => #&(1 2 3)

See section 11.2.4 for more information about immediate boxes.

MzScheme defines the unquote and unquote-splicing identifiers as top-level syntactic forms that always report a syntax error. The quasiquote form recognizes normal unquote and unquote-splicing uses via module-identifier=?. (See section 12.3.1 for more information on identifier comparisons.)

2.8  Binding Forms

2.8.1  Definitions

A procedure definition

(define variable (lambda formals expr ···1))

can be abbreviated

(define (variable . formals) expr ···1)

In addition to this standard Scheme abbreviation, MzScheme supports an MIT-style generalization, so that a definition

(define header (lambda formals expr ···1))

can be abbreviated

(define (header . formals) expr ···1))

even if header is itself a parenthesized procedure abbreviation. The general syntax of define is as follows:

(define variable expr)
(define (header . formals) expr ···1)

header is one of
  variable
  (header . formals)

formals is one of
  variable
  (variable ···)
  (variable variable ··· . variable)

Multiple values can be bound to multiple variables at once using define-values:

(define-values (variable ···) expr)

The number of values returned by expr must match the number of variables provided, and the variables must be distinct. No procedure-definition abbreviation is available for define-values.

Examples:

(define x 1) 
x ; => 1
(define (f x) (+ x 1))
(f 2) ; => 3
(define (((g x) y z) . w) (list x y z w))
(let ([h ((g 1) 2 3)])
  (list (h 4 5) (h 6))) ; => '((1 2 3 (4 5)) (1 2 3 (6)))

(define-values (x) 2) 
x ; => 2
(define-values (x y) (values 3 4)) 
x ; => 3
y ; => 4
(define-values (x y) (values 5 (add1 x))) 
y ; => 4
(define-values () (values)) ; same as (void)
(define x (values 7 8)) ;  => exn:fail:contract:arity, 2 values for 1-value context
(define-values (x y) 7) ;  => exn:fail:contract:arity, 1 value for 2-value context
(define-values () 7) ;  => exn:fail:contract:arity, 1 value for 0-value context

2.8.2  Local Bindings

Local variables are bound with standard Scheme's let, let*, and letrec. MzScheme's letrec form guarantees sequential left-to-right evaluation of the binding expressions. (The letrec bound in the result of (scheme-report-environment 5), however, is defined exactly as in R5RS.)

Multiple values are bound to multiple local variables at once with let-values, let*-values, and letrec-values. The syntax for let-values is:

(let-values (((variable ···) expr) ···) body-expr ···1)

As in define-values, the number of values returned by each expr must match the number of variables declared in the corresponding clause. Each expr remains outside of the scope of all variables bound by the let-values expression.

The syntax for let*-values and letrec-values is the same as for let-values, and the binding semantics for each form corresponds to the single-value binding form:

When a letrec or letrec-values expression is evaluated, each variable binding is initially assigned the special undefined value (see section 3.1); the undefined value is replaced after the corresponding expression is evaluated.

Examples:

(define x 0) 
(let ([x 5] [y x]) y) ; => 0
(let* ([x 5] [y x]) y) ; => 5
(letrec ([x 5] [y x]) y) ; => 5
(letrec ([x y] [y 5]) x) ; => undefined
(let-values ([(x) 5] [(y) x]) y) ; => 0
(let-values ([(x y) (values 5 x)]) y) ; => 0
(let*-values ([(x) 5] [(y) x]) y) ; => 5
(let*-values ([(x y) (values 5 x)]) y) ; => 0
(letrec-values ([(x) 5] [(y) x]) y) ; => 5
(letrec-values ([(x y) (values 5 x)]) y) ; => undefined
(letrec-values ([(odd even) (values 
                             (lambda (n) (if (zero? n) #f (even (sub1 n)))) 
                             (lambda (n) (if (zero? n) #t (odd (sub1 n)))))]) 
   (odd 17)) ; => #t

2.8.3  Assignments

The standard set! form assigns a value to a single global, local, or module variable. Multiple variables can be assigned at once using set!-values:

(set!-values (variable ···) expr)

The number of values returned by expr must match the number of variables provided.

The variables, which must be distinct, can be any mixture of global, local, and module variables. Assignments are performed sequentially from the first variable to the last. If an error occurs in one of the assignments (perhaps because a global variable is not yet bound), then the assignments for the preceding variables will have already completed, but assignments for the remaining variables will never complete.

2.8.4  Fluid-Let

The syntax for a fluid-let expression is the same as for let:

(fluid-let ((variable expr) ···) body-expr ···1)

Each variable must be either a local variable or a global or module variable that is bound before the fluid-let expression is evaluated. Before the body-exprs are evaluated, the bindings for the variables are set! to the values of the corresponding exprs. Once the body-exprs have been evaluated, the values of the variables are restored. The value of the entire fluid-let expression is the value of the last body-expr.

2.8.5  Syntax Expansion and Internal Definitions

All binding forms are syntax-expanded into define-values, let-values, letrec-values, define-syntaxes, and letrec-syntaxes+values expressions. The set!-values form is expanded to let-values with set!. See section 12.6.1 for more information.

All define-values expressions that are inside only begin expressions are treated as top-level definitions. Body define-values expressions in a module expression are handled specially as described in section 5.1. Any other define-values expression is either an internal definition or syntactically illegal. The same is true of define-syntaxes expressions.

Internal definitions can appear at the start of a sequence of expressions, such as the start of a lambda, case-lambda, or let body. At least one non-definition expression must follow a sequence of internal definitions. The first expression in a begin0 expression cannot be an internal definition; for the purposes of internal definitions, the second expression is the start of the sequence.

When a begin expression appears within a sequence, its content is inlined into the sequence (recursively, if the begin expression contains other begin expressions). Like top-level begin expressions (and unlike other begin expressions), a begin expression within an internal definition sequence can be empty.

An internal define-values or define-syntaxes expression is transformed, along with the expressions following it, into a letrec-syntaxes+values expression: the identifiers bound by the internal definitions become the binding identifiers of the new letrec-syntaxes+values expression, and the expressions that follow the definitions become the body of the new letrec-syntaxes+values expression.

Multiple adjacent definitions are collected into a single letrec-syntaxes+values transformation, so that the definitions can be mutually recursive, but the definitions expressions must be adjacent. A non-definition marks the start of a sequence of expressions to be moved into the body of the newly created letrec-syntaxes+values form.

Internal definitions are detected after a partial syntax expansion that stops at core forms, and thus exposes begin, define-values, and define-syntaxes. Forms are expanded left to right, and whenever a definition is discovered, a binding is introduced immediately for further expansion, so a definition can shadow variables when later forms are expanded. Furthermore, when a define-syntaxes form is discovered, the right-hand side is immediately evaluated, and the result is bound as syntax to the corresponding identifier(s); thus, a locally defined macro can be used to generate later definitions in the same internal-definition context.

2.9  Case-Lambda

The case-lambda form creates a procedure that dispatches to a particular body of expressions based on the number of arguments that the procedure receives. The case-lambda form provides a mechanism for creating variable-arity procedures with more control and efficiency than using a lambda ``rest argument,'' such as the x in (lambda (a . x) expr ···1).

A case-lambda expression has the form:

(case-lambda
  (formals expr ···1)
  ···)

formals is one of 
  variable
  (variable ···)
  (variable ··· . variable)

Each (formals expr ···1) clause of a case-lambda expression is analogous to a lambda expression of the form (lambda formals expr ···1). The scope of the variables in each clause's formals includes only the same clause's exprs. The formals variables are bound to actual arguments in an application in the same way that lambda variables are bound in an application.

When a case-lambda procedure is invoked, one clause is selected and its exprs are evaluated for the application; the result of the last expr in the clause is the result of the application. The clause that is selected for an application is the first one with a formals specification that can accommodate the number of arguments in the application.3

Examples:

(define f
  (case-lambda
    [(x) x]
    [(x y) (+ x y)]
    [(a . any) a]))
(f 1) ; => 1
(f 1 2) ; => 3
(f 4 5 6 7) ; => 4
(f) ; => raises exn:fail:contract:arity

The result of a case-lambda expression is a procedure, just like the result of a lambda expression. Thus, the procedure? predicate returns #t when applied to the result of a case-lambda expression.

2.10  Procedure Application

The ``empty application'' form () expands to the quoted empty list '().

2.11  Variable Reference

The #%variable-reference form returns a value representing the address of a top-level or module variable:

(#%variable-reference variable)
(#%variable-reference (#%top . variable))

In the non-#%top form, a syntax error is reported if variable is not bound to a top-level or module variable.

The result of a #%variable-reference expression is opaque, with no useful operation in MzScheme. See Inside PLT MzScheme for information on its use in low-level extensions to MzScheme.


3 It is possible that a clause in a case-lambda expression can never be evaluated because a preceding clause always matches the arguments.