397 lines
14 KiB
Racket
397 lines
14 KiB
Racket
#reader(lib "docreader.ss" "scribble")
|
|
@require["mz.ss"]
|
|
|
|
@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, normally, 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 result(s) of the application
|
|
expression is(are) the result(s) of the procedure.
|
|
|
|
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)
|
|
]}
|
|
|
|
|
|
@;------------------------------------------------------------------------
|
|
@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 arguments than it accepts, the @exnraise[exn:fail:contract]. If
|
|
@scheme[gen-formals] includes @scheme[keyword]s and an application
|
|
includes too few arguments before the keyword section, the same
|
|
keyword in multiple positions, or a keyword that is not among the
|
|
@scheme[gen-formals] @scheme[_keyword]s, 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)))
|
|
]}
|
|
|
|
@;------------------------------------------------------------------------
|
|
@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
|
|
availablek. 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.
|
|
|
|
@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. 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{Sequencing: @scheme[begin]}
|
|
|