racket/collects/scribblings/reference/syntax.scrbl
Matthew Flatt 2fe7c75dc1 doc work
svn: r6692
2007-06-19 08:32:38 +00:00

550 lines
19 KiB
Racket

#reader(lib "docreader.ss" "scribble")
@require["mz.ss"]
@define[cvt (schemefont "CVT")]
@title[#:tag "mz:syntax" #:style 'toc]{Core Syntactic Forms}
This section describes core syntax forms that apear in a fully
expanded expression, plus a few closely-related non-core forms.
@local-table-of-contents[]
@subsubsub*section{Notation}
Each syntactic form is described by a BNF-like notation that describes
a combination of (syntax-wrapped) pairs, symbols, and other data (not
a sequence of characters). These grammatical specifications are shown
as follows:
@specsubform[(#, @schemekeywordfont{some-form} id ...)]
Within such specifications,
@itemize{
@item{@scheme[...] indicates zero or more
repetitions of the preceding datum.}
@item{@scheme[...+] indicates one or
more repetitions of the preceding datum.}
@item{italic meta-identifiers play the role of non-terminals; in
particular,
@itemize{
@item{a meta-identifier that ends in @scheme[_id] stands for an
identifier.}
@item{a meta-identifier that ends in @scheme[_keyword] stands
for a keyword.}
@item{a meta-identifier that ends with @scheme[_expr] stands
for a sub-form that is expanded as an expression.}
@item{A meta-identifier that ends with @scheme[_body] stands
for a sub-form that is expanded in an
internal-definition context (see
@secref["mz:intdef-body"]).}
}} }
@;------------------------------------------------------------------------
@section{Variable References and @scheme[#%top]}
@defform/none[id]{
Refers to a module-level or local binding, when @scheme[id] is
not bound as a transformer (see @secref["mz:expansion"]). At run-time,
the reference evaluates to the value in the location associated with
the binding.
When the expander encounters an @scheme[id] that is not bound by a
module-level or local binding, it converts the expression to @scheme[(#,
@schemeidfont{#%top} . id)] giving @schemeidfont{#%top} the lexical
context of the @scheme[id]; typically, that context refers to
@scheme[#%top].
@examples[
(define x 10)
x
(let ([x 5]) x)
((lambda (x) x) 2)
]}
@defform[(#%top . id)]{
Refers to a top-level definition that could bind @scheme[id],
even if @scheme[id] has a local binding in its context. Such
references are disallowed anywhere within a @scheme[module] form.
@examples[
(define x 12)
(let ([x 5]) (#%top . x))
]}
@;------------------------------------------------------------------------
@section[#:tag "mz:application"]{Procedure Applications and @scheme[#%app]}
@guideintro["guide:application"]{procedure applications}
@defform/none[(proc-expr arg ...)]{
Applies a procedure, when @scheme[proc-expr] is not an
identifier that has a transformer binding (see
@secref["mz:expansion"]).
More precisely, the expander converts this form to @scheme[(#,
@schemeidfont{#%app} proc-expr arg ...)], giving @schemeidfont{#%app}
the lexical context that is associated with the original form (i.e.,
the pair that combines @scheme[proc-expr] and its
arguments). Typically, the lexical context of the pair indicates the
procedure-application @scheme[#%app] that is described next.
@examples[
(+ 1 2)
((lambda (x #:arg y) (list y x)) #:arg 2 1)
]}
@defform[(#%app proc-expr arg ...)]{
Applies a procedure. Each @scheme[arg] is one of the following:
@specsubform[arg-expr]{The resulting value is a non-keyword
argument.}
@specsubform[(code:line keyword arg-expr)]{The resulting value is a
keyword argument using @scheme[keyword]. Each
@scheme[keyword] in the application must be distinct.}
The @scheme[proc-expr] and @scheme[_arg-expr]s are evaluated in order,
left to right. If the result of @scheme[proc-expr] is a procedure that
accepts as many arguments as non-@scheme[_keyword]
@scheme[_arg-expr]s, if it accepts arguments for all of the
@scheme[_keyword]s in the application, and if all required
keyword-based arguments are represented among the @scheme[_keyword]s
in the application, then the procedure is called with the values of
the @scheme[arg-expr]s. Otherwise, the @exnraise[exn:fail:contract].
The continuation of the procedure call is the same as the continuation
of the application expression, so the results of the procedure are the
results of the application expression.
The relative order of @scheme[_keyword]-based arguments matters only
for the order of @scheme[_arg-expr] evaluations; the arguments are
associated with argument variables in the applied procedure based on
the @scheme[_keyword]s, and not their positions. The other
@scheme[_arg-expr] values, in contrast, are associated with variables
according to their order in the application form.
@examples[
(#%app + 1 2)
(#%app (lambda (x #:arg y) (list y x)) #:arg 2 1)
(#%app cons)
]}
@defform[(#%plain-app proc-expr arg-expr ...)]{
Like @scheme[#%app], but without support for keyword arguments.
}
@;------------------------------------------------------------------------
@section[#:tag "mz:lambda"]{Procedure Expressions: @scheme[lambda] and @scheme[case-lambda]}
@guideintro["guide:lambda"]{procedure expressions}
@defform/subs[(lambda gen-formals body ...+)
([formals (id ...)
(id ...+ . rest-id)
rest-id]
[gen-formals formals
(arg ...)
(arg ...+ . rest-id)]
[arg id
[id default-expr]
(code:line keyword id)
(code:line keyword [id default-expr])])]{
Produces a procedure. The @scheme[gen-formals] determines the number of
arguments that the procedure accepts. It is either a simple
@scheme[formals] or one of the extended forms.
A simple @scheme[_formals] has one of the following three forms:
@specsubform[(id ...)]{ The procedure accepts as many non-keyword
argument values as the number of @scheme[id]s. Each @scheme[id]
is associated with an argument value by position.}
@specsubform[(id ...+ . rest-id)]{ The procedure accepts any number of
non-keyword arguments greater or equal to the number of
@scheme[id]s. When the procedure is applied, the @scheme[id]s
are associated with argument values by position, and all
leftover arguments are placed into a list that is associated to
@scheme[rest-id].}
@specsubform[rest-id]{ The procedure accepts any number of non-keyword
arguments. All arguments are placed into a list that is
associated with @scheme[rest-id].}
Besides the three @scheme[formals] forms, a @scheme[gen-formals] can be
a sequence of @scheme[arg]s optionally ending with a
@scheme[rest-id]:
@specsubform[(arg ...)]{ Each @scheme[arg] has the following
four forms:
@specsubform[id]{Adds one to both the minimum and maximum
number of non-keyword arguments accepted by the procedure. The
@scheme[id] is associated with an actual argument by
position.}
@specsubform[[id default-expr]]{Adds one to the maximum number
of non-keyword arguments accepted by the procedure. The
@scheme[id] is associated with an actual argument by position,
and if no such argument is provided, the @scheme[default-expr]
is evaluated to produce a value associated with @scheme[id].
No @scheme[arg] with a @scheme[default-expr] can appear
before an @scheme[id] without a @scheme[default-expr] and
without a @scheme[keyword].}
@specsubform[(code:line keyword id)]{The procedure requires a
keyword-based argument using @scheme[keyword]. The @scheme[id]
is associated with a keyword-based actual argument using
@scheme[keyword].}
@specsubform[(code:line keyword [id default-expr])]{The
procedure accepts a keyword-based using @scheme[keyword]. The
@scheme[id] is associated with a keyword-based actual argument
using @scheme[keyword], if supplied in an application;
otherwise, the @scheme[default-expr] is evaluated to obtain a
value to associate with @scheme[id].}
The position of a @scheme[_keyword] @scheme[arg] in
@scheme[gen-formals] does not matter, but each specified
@scheme[_keyword] must be distinct.}
@specsubform[(arg ...+ . rest-id)]{ Like the previous case, but
the procedure accepts any number of non-keyword arguments
beyond its minimum number of arguments. When more arguments are
provided than non-@scheme[_keyword] arguments among the
@scheme[arg]s, the extra arguments are placed into a
list that is associated to @scheme[rest-id].}
The @scheme[gen-formals] identifiers are bound in the @scheme[body]s. When
the procedure is applied, a new location is created for each
identifier, and the location is filled with the associated argument
value.
If any identifier appears in the @scheme[body]s that is not one of the
identifiers in @scheme[gen-formals], then it refers to the same location
that it would if it appeared in place of the @scheme[lambda]
expression. (In other words, variable reference is lexically scoped.)
When multiple identifiers appear in a @scheme[gen-formals], they must be
distinct according to @scheme[bound-identifier=?].
If the procedure procedure by @scheme[lambda] is applied to fewer or
more by-position or arguments than it accepts, to by-keyword arguments
that it does not accept, or without required by-keyword arguments, then
the @exnraise[exn:fail:contract].
The last @scheme[body] expression is in tail position with respect to
the procedure body.
@examples[
((lambda (x) x) 10)
((lambda (x y) (list y x)) 1 2)
((lambda (x [y 5]) (list y x)) 1 2)
(let ([f (lambda (x #:arg y) (list y x))])
(list (f 1 #:arg 2)
(f #:arg 2 1)))
]}
@defform[(case-lambda [formals body ...+] ...)]{
Produces a procedure. Each @scheme[[forms body ...+]]
clause is analogous to a single @scheme[lambda] procedure; applying
the @scheme[case-lambda]-generated procedure is the same as applying a
procedure that corresponds to one of the clauses---the first procedure
that accepts the given number of arguments. If no corresponding
procedure accepts the given number of arguments, the
@exnraise[exn:fail:contract].
Note that a @scheme[case-lambda] clause supports only
@scheme[formals], not the more general @scheme[_gen-formals] of
@scheme[lambda]. That is, @scheme[case-lambda] does not directly
support keyword and optional arguments.
@examples[
(let ([f (case-lambda
[() 10]
[(x) x]
[(x y) (list y x)]
[r r])])
(list (f)
(f 1)
(f 1 2)
(f 1 2 3)))
]}
@defform[(#%plain-lambda formals body ...+)]{
Like @scheme[lambda], but without support for keyword or optional arguments.
}
@;------------------------------------------------------------------------
@section{Local Binding: @scheme[let], @scheme[let*], and @scheme[letrec]}
@defform*[[(let ([id val-expr] ...) body ...+)
(let proc-id ([id init-expr] ...) body ...+)]]{
The first form evaluates the @scheme[val-expr]s left-to-right, creates
a new location for each @scheme[id], and places the values into the
locations. It then evaluates the @scheme[body]s, in which the
@scheme[id]s are bound. The last @scheme[body] expression is in
tail position with respect to the @scheme[let] form. The @scheme[id]s
must be distinct according to @scheme[bound-identifier=?].
@examples[
(let ([x 5]) x)
(let ([x 5])
(let ([x 2]
[y x])
(list y x)))
]
The second form evaluates the @scheme[init-expr]s; the resulting
values become arguments in an application of a procedure
@scheme[(lambda (id ...) body ...+)], where @scheme[proc-id] is bound
within the @scheme[body]s to the procedure itself.}
@examples[
(let fac ([n 10])
(if (zero? n)
1
(* n (fac (sub1 n)))))
]
@defform[(let* ([id val-expr] ...) body ...+)]{
Similar to @scheme[let], but evaluates the @scheme[val-expr]s one by
one, creating a location for each @scheme[id] as soon as the value is
available. The @scheme[id]s are bound in the remaining @scheme[val-expr]s
as well as the @scheme[body]s, and the @scheme[id]s need not be
distinct; later bindings shadow earlier bindings.
@examples[
(let ([x 1]
[y (+ x 1)])
(list y x))
]}
@defform[(letrec ([id val-expr] ...) body ...+)]{
Similar to @scheme[let], but the locations for all @scheme[id]s are
created first and filled with @|undefined-const|, and all
@scheme[id]s are bound in all @scheme[val-expr]s as well as the
@scheme[body]s. The @scheme[id]s must be distinct according to
@scheme[bound-identifier=?].
@examples[
(letrec ([is-even? (lambda (n)
(or (zero? n)
(is-odd? (sub1 n))))]
[is-odd? (lambda (n)
(or (= n 1)
(is-even? (sub1 n))))])
(is-odd? 11))
]}
@defform[(let-values ([(id ...) val-expr] ...) body ...+)]{ Like
@scheme[let], except that each @scheme[val-expr] must produce as many
values as corresponding @scheme[id]s, otherwise the
@exnraise[exn:fail:contract]. A separate location is created for each
@scheme[id], all of which are bound in the @scheme[body]s.
@examples[
(let-values ([(x y) (quotient/remainder 10 3)])
(list y x))
]}
@defform[(let*-values ([(id ...) val-expr] ...) body ...+)]{ Like
@scheme[let*], except that each @scheme[val-expr] must produce as many
values as corresponding @scheme[id]s. A separate location is created
for each @scheme[id], all of which are bound in the later
@scheme[val-expr]s and in the @scheme[body]s.
@examples[
(let*-values ([(x y) (quotient/remainder 10 3)]
[(z) (list y x)])
z)
]}
@defform[(letrec-values ([(id ...) val-expr] ...) body ...+)]{ Like
@scheme[letrec], except that each @scheme[val-expr] must produce as
many values as corresponding @scheme[id]s. A separate location is
created for each @scheme[id], all of which are initialized to
@|undefined-const| and bound in all @scheme[val-expr]s
and in the @scheme[body]s.
@examples[
(letrec-values ([(is-even? is-odd?)
(values
(lambda (n)
(or (zero? n)
(is-odd? (sub1 n))))
(lambda (n)
(or (= n 1)
(is-even? (sub1 n)))))])
(is-odd? 11))
]}
@;------------------------------------------------------------------------
@section[#:tag "mz:if"]{Conditionals: @scheme[if]}
@defform[(if test-expr then-expr else-expr)]{
Evaluates @scheme[test-expr]. If it produces any value other than
@scheme[#f], then @scheme[then-expr] is evaluated, and its results are
the result for the @scheme[if] form. Otherwise, @scheme[else-expr] is
evaluated, and its results are the result for the @scheme[if]
form. The @scheme[then-expr] and @scheme[else-expr] are in tail
position with respect to the @scheme[if] form.
@examples[
(if (positive? -5) (error "doesn't get here") 2)
(if (positive? 5) 1 (error "doesn't get here"))
]}
@;------------------------------------------------------------------------
@section[#:tag "mz:define"]{Definitions: @scheme[define] and @scheme[define-values]}
@defform*/subs[[(define id expr)
(define (head args) body ...+)]
([head id
(head args)]
[args (code:line arg ...)
(code:line arg ... #, @schemeparenfont{.} rest-id)]
[arg arg-id
[arg-id default-expr]
(code:line keyword arg-id)
(code:line keyword [arg-id default-expr])])]{
The first form binds @scheme[id] to the result of @scheme[expr], and
the second form binds @scheme[id] to a procedure. In the second case,
the generation procedure is @scheme[(#,cvt (head args) body ...+)],
using the @|cvt| meta-function defined as follows:
@schemeblock[
(#,cvt (id . _gen-formals) . _datum) = (lambda _gen-formals . _datum)
(#,cvt (head . _gen-formals) . _datum) = (lambda _gen-formals expr)
#, @elem{if} (#,cvt head . _datum) = expr
]
At the top level, the top-level binding @scheme[id] is created after
evaluating @scheme[expr], if it does not exist already, and the
top-level mapping of @scheme[id] (see @secref["mz:namespace"]) is set
to the binding at the same time.
@defexamples[
(define x 10)
x
]
@def+int[
(define (f x)
(+ x 1))
(f 10)
]
@def+int[
(define ((f x) [y 20])
(+ x y))
((f 10) 30)
((f 10))
]
}
@defform[(define-values (id ...) expr)]{
Evaluates the @scheme[expr], and binds the results to the
@scheme[id]s, in order, if the number of results matches the number of
@scheme[id]s; if @scheme[expr] produces a different number of results,
the @exnraise[exn:fail:contract].
At the top level, the top-level binding for each @scheme[id] is
created after evaluating @scheme[expr], if it does not exist already,
and the top-level mapping of each @scheme[id] (see
@secref["mz:namespace"]) is set to the binding at the same time.
@defexamples[
(define-values () (values))
(define-values (x y z) (values 1 2 3))
z
]
}
@;------------------------------------------------------------------------
@section{Sequencing: @scheme[begin] and @scheme[begin0]}
@defform*[[(begin form ...)
(begin expr ...+)]]{
The first form applies when @scheme[begin] appears at the top level,
at module level, or in an internal-definition position (before any
expression in the internal-definition sequence). In that case, the
@scheme[begin] form is equivalent to splicing the @scheme[form]s into
the enclosing context.
The second form applies for @scheme[begin] in an expression position.
In that case, the @scheme[expr]s are evaluated in order, and the
results are ignored for all but the last @scheme[expr]. The last
@scheme[expr] is in tail position with respect to the @scheme[begin]
form.
@examples[
(begin
(define x 10)
x)
(+ 1 (begin
(printf "hi\n")
2))
(let-values ([(x y) (begin
(values 1 2 3)
(values 1 2))])
(list x y))
]}
@defform[(begin0 expr body ...+)]{
Evaluates the @scheme[expr], then evaluates the @scheme[body]s,
ignoring the @scheme[body] results. The results of the @scheme[expr]
are the results of the @scheme[begin0] form, but the @scheme[expr] is
in tail position only if no @scheme[body]s are present.
@examples[
(begin0
(values 1 2)
(printf "hi\n"))
]}
@;------------------------------------------------------------------------
@section{Continuation Marks: @scheme[with-continuation-marks]}
@defform[(with-continuation-mark key-expr val-expr result-expr)]{
Evaluates @scheme[key-expr] and @scheme[val-expr] in order to obtain a key and
value, respectively. The key and value are attached as a mark to the
current continuation frame (see @secref["mz:contmarks"]), and then
@scheme[result-expr] is evaluated in tail position.
}
@;------------------------------------------------------------------------
@section{Syntax Quoting: @scheme[quote-syntax]}
@defform[(quote-syntax datum)]{
Produces a syntax object that preserves
lexical and source-location information attached to @scheme[datum]
at expansion time.
@examples[
(syntax? (quote-syntax x))
]
}