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
and
values
procedure, and also provides binding forms for
multiple-value expressions, discussed in section 2.8.call-with-values
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
can return any number of values, but the procedure
supplied to for-each
must return a single value.map
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)) ; => error: returned 2 values to single-value context (- (values
)) ; => error: 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:
(when test-expr expr ···1)
evaluates theexpr
body expressions only whentest-expr
returns a true value.(unless test-expr expr ···1)
evaluates theexpr
body expressions only whentest-expr
returns#f
.
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
As a top-level form, begin
wraps each sub-expression but
the last with a prompt (see section 6.5) using the default prompt tag
and an abort handler that re-aborts. This wrapping helps maintains the
equivalence between wrapping a sequence top-level forms with a begin
and splicing the sequence into the enclosing context.
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))) ; =>s
display
9
then returns4
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
.
Nevertheless, a quoted cons cell, vector, or list is mutable;
mutations to the result of a eq?
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
. (See section 12.3.1 for more
information on identifier comparisons.)module-identifier=?
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
variable
s provided, and the variable
s 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(
(define x (void
)values
7 8)) ; => error: 2 values for 1-value context (define-values (x y) 7) ; => error: 1 value for 2-value context (define-values () 7) ; => error: 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
, however, is
defined exactly as in R5RS.)5
)
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 variable
s 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:
In a
let*-values
expression, the scope of the variables of each clause includes all of the remaining binding clauses. The clause expressions are evaluated and bound to variables sequentially.In a
letrec-values
expression, the scope of the variables of each clause includes all of the binding clauses. The clause expressions are evaluated and bound to variables sequentially.
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
variable
s provided.
The variable
s, 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
variable
s will have already completed, but assignments for the
remaining variable
s 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-expr
s are evaluated, the
bindings for the variable
s are set!
to the values of the
corresponding expr
s. Once the body-expr
s 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
variable
s in each clause's formals
includes only the same
clause's expr
s. 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 expr
s 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) ; => raisesexn: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))
For either 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.
2.12 Forcing Expression Parsing
In certain contexts, the expansion and parsing of a syntactic
form can differ depending on whether it is parsed as an expression or a
top-level form. For example, at the top level, begin
acts
as a splicing form that wraps each sub-expression evaluation with
a prompt (see section 6.5), but no such prompts are inserted
for begin
parsed as a sequencing expression.
The #%expression
form wraps a datum
so that it
must be parsed as an expression:
(#%expression expr)
A fully-expanded expression eliminates #%expression
except
as a top-level form.
3 It is possible that
a clause in a case-lambda
expression can never be evaluated
because a preceding clause always matches the arguments.