#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]}