doc edits
svn: r6708
This commit is contained in:
parent
8a7caa749e
commit
ceb1b78cea
|
@ -62,6 +62,7 @@
|
|||
(printf "\\definecolor{LightGray}{rgb}{0.90,0.90,0.90}\n")
|
||||
(printf "\\newcommand{\\schemeinput}[1]{\\colorbox{LightGray}{\\hspace{-0.5ex}\\schemeinputcol{#1}\\hspace{-0.5ex}}}\n")
|
||||
(printf "\\newcommand{\\highlighted}[1]{\\colorbox{PaleBlue}{\\hspace{-0.5ex}\\schemeinputcol{#1}\\hspace{-0.5ex}}}\n")
|
||||
(printf "\\newcommand{\\techlink}[1]{#1}\n")
|
||||
(printf "\\begin{document}\n")
|
||||
(when (part-title-content d)
|
||||
(printf "\\title{")
|
||||
|
|
|
@ -151,6 +151,28 @@
|
|||
|
||||
;; ----------------------------------------
|
||||
|
||||
(provide deftech tech)
|
||||
|
||||
(define (*tech make-elem style s)
|
||||
(let* ([c (decode-content s)]
|
||||
[s (regexp-replace* #px"[-\\s]+"
|
||||
(regexp-replace
|
||||
#rx"s$"
|
||||
(string-foldcase (content->string c))
|
||||
"")
|
||||
" ")])
|
||||
(make-elem style
|
||||
c
|
||||
(format "tech-term:~a" s))))
|
||||
|
||||
(define/kw (deftech #:body s)
|
||||
(*tech make-target-element #f (list (apply defterm s))))
|
||||
|
||||
(define/kw (tech #:body s)
|
||||
(*tech make-link-element "techlink" s))
|
||||
|
||||
;; ----------------------------------------
|
||||
|
||||
(provide defproc defproc* defstruct defthing defform defform* defform/subs defform*/subs defform/none
|
||||
specform specform/subs
|
||||
specsubform specspecsubform specsubform/inline
|
||||
|
|
|
@ -246,6 +246,15 @@
|
|||
color: red;
|
||||
}
|
||||
|
||||
.techlink {
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
.techlink:hover {
|
||||
text-decoration: underline;
|
||||
color: blue;
|
||||
}
|
||||
|
||||
.schemeresult {
|
||||
color: #0000af;
|
||||
font-family: Courier; font-size: 80%;
|
||||
|
|
|
@ -1,202 +1,4 @@
|
|||
#reader(lib "docreader.ss" "scribble")
|
||||
@require["mz.ss"]
|
||||
|
||||
@title[#:tag "mz:expansion"]{Syntax Expansion}
|
||||
|
||||
Expansion recursively processes a syntax object to parse it. In
|
||||
general, the parsing of a datum depends on its outermost shape:
|
||||
|
||||
@itemize{
|
||||
|
||||
@item{If it is a syntax-object symbol, also known as an
|
||||
@defterm{identifier}, then a binding is determined using symbol
|
||||
along with the lexical information in the identifier. The
|
||||
binding determines the next parsing step.}
|
||||
|
||||
@item{If it is a syntax-object pair whose first element is an
|
||||
identifier, then the identifier's binding is used (as in the
|
||||
preceding case).}
|
||||
|
||||
@item{If it is a syntax-object pair, then a new syntax-object symbol
|
||||
@scheme['#%app] is created using the lexical context of the
|
||||
pair. If the resulting @scheme[#%app] identifier has no
|
||||
binding, parsing fails with an @scheme[exn:fail:syntax]
|
||||
exception. Otherwise, the new identifier is combined with the
|
||||
original pair to form a new syntax-object pair (using the same
|
||||
context as the original pair), and parsing starts again (i.e.,
|
||||
it continues with the preceding case).}
|
||||
|
||||
@item{If it is any other syntax object, then a new syntax-object
|
||||
symbol @scheme['#%datum] is created using the lexical context
|
||||
of the original syntax object. If the resulting
|
||||
@scheme[#%datum] identifier has no binding, parsing fails with
|
||||
an @scheme[exn:fail:syntax] exception. Otherwise, the new
|
||||
identifier is combined with the original syntax object in a new
|
||||
syntax-object pair (using the same context as the original
|
||||
pair), and parsing starts again (i.e., it continues with the
|
||||
second case above).}
|
||||
|
||||
}
|
||||
|
||||
For either of the first two steps, if the identifier has no binding,
|
||||
then a new syntax-object symbol @scheme['#%top] is created using the
|
||||
lexical context of the identifier; if this @scheme[#%top] identifier
|
||||
has no binding, then parsing fails with an @scheme[exn:fail:syntax]
|
||||
exception. Otherwise, the new identifier is combined with the
|
||||
original identifier in a new syntax-object pair (using the same
|
||||
context as the original identifier), and parsing starts again.
|
||||
|
||||
Thus, the possibilities that do not fail lead to an identifier with a
|
||||
particular binding. This binding refers to one of three things:
|
||||
|
||||
@itemize{
|
||||
|
||||
@item{A transformer binding, such as introduced by
|
||||
@scheme[define-syntax] or @scheme[let-syntax]. If the
|
||||
associated value is to a procedure of one argument, the
|
||||
procedure is called as a syntax transformer (see
|
||||
@secref["transformers"]), and parsing starts again with the
|
||||
syntax-object result. If the transformer binding is to any
|
||||
other kind of value, parsing fails with an
|
||||
@scheme[exn:fail:syntax] exception.}
|
||||
|
||||
@item{A variable binding, such as introduced by a module-level
|
||||
@scheme[define] or by @scheme[let]. In this case, if the form
|
||||
being parsed is just an identifier, then it is parsed as a
|
||||
run-time reference to the location of the corersponding
|
||||
variable. If the form being parsed is a syntax-object list,
|
||||
then an @scheme[#%app] is added to the front of the
|
||||
syntax-object list in the same way as when the first item in
|
||||
the syntax-object list is not an identifier (third case in the
|
||||
previous enumeration), and parsing continues.}
|
||||
|
||||
@item{Core syntax, which is parsed as described in
|
||||
@secref["mz:syntax"]. Parsing core syntactic forms typically
|
||||
involves recursive parsing of sub-forms, and may introduce
|
||||
bindings that control the parsing of sub-forms.}
|
||||
|
||||
}
|
||||
|
||||
Each expansion step occurs in a particular context, and transformers
|
||||
and core-syntax parsing can depend on the context. For example, a
|
||||
@scheme[module] form is allowed only in a top-level context. The
|
||||
possible contexts are as follows:
|
||||
|
||||
@itemize{
|
||||
|
||||
@item{@defterm{top level} : outside of any module, definition, or
|
||||
expression, except that sub-expressions of a top-level
|
||||
@scheme[begin] form are also expanded as top-level forms.}
|
||||
|
||||
@item{@defterm{module begin} : inside the body of a module, as the
|
||||
only form within the module.}
|
||||
|
||||
@item{@defterm{module body} : in the body of a module (inside the
|
||||
moudule-begin layer).}
|
||||
|
||||
@item{@defterm{internal definition} : in a nested context that allows
|
||||
both definitions and expressions.}
|
||||
|
||||
@item{@defterm{expression} : in a context where only expressions are
|
||||
allowed.}
|
||||
|
||||
}
|
||||
|
||||
Different core syntax forms parse sub-forms in different contexts. For
|
||||
example, a @scheme[let] form always parses the right-hand expressions
|
||||
of a binding in an expression context, but it starts parsing the body
|
||||
in an internal-definition context.
|
||||
|
||||
@section[#:tag "mz:intdef-body"]{Internal Definitions}
|
||||
|
||||
An internal-definition context corresponds to a partial expansion
|
||||
step. A form that supports internal definitions starts by expanding
|
||||
its first form in an internal-definition context, but only
|
||||
partially. That is, it recursively expands only until the form becomes
|
||||
one of the following:
|
||||
|
||||
@itemize{
|
||||
|
||||
@item{A @scheme[define-values] or @scheme[define-syntaxes] form: The
|
||||
definition form is not expanded further. Instead, the next form
|
||||
is expanded partially, and so on. As soon as an expression form
|
||||
is found, the accumulated definition forms are converted to a
|
||||
@scheme[letrec-values] (if no @scheme[define-syntaxes] forms
|
||||
were found) or @scheme[letrec-syntaxes+values] form, moving the
|
||||
expression forms to the body to be expanded in expression
|
||||
context.
|
||||
|
||||
When a @scheme[define-values] form is discovered, the lexical
|
||||
context of all syntax objects for the body sequence is
|
||||
immediately enriched with bindings for the
|
||||
@scheme[define-values] form before expansion continues. When a
|
||||
@scheme[define-syntaxes] form is discovered, the right-hand
|
||||
side is executed and a transformer binding is installed before
|
||||
expansion continues.}
|
||||
|
||||
@item{A primitive expression form other than @scheme[begin]: The
|
||||
expression will be further expanded in an expression context,
|
||||
along with all remaining body forms. If any definitions were
|
||||
found, this expansion takes place after conversion to a
|
||||
@scheme[letrec-values] or @scheme[letrec-syntaxes+values]
|
||||
form. Otherwise, the expressions are expanded immediately in an
|
||||
expression context.}
|
||||
|
||||
@item{A @scheme[begin] form: The sub-forms of the @scheme[begin] are
|
||||
spliced into the internal-definition sequence, and partial
|
||||
expansion continues with the first of the newly-spliced forms
|
||||
(or the next form, if the @scheme[begin] had no sub-forms).}
|
||||
|
||||
}
|
||||
|
||||
@section[#:tag "mz:fully-expanded"]{Fully Expanded Programs}
|
||||
|
||||
A fully expanded program---that is, a parsed program---is represented
|
||||
in the same way as an unparsed program: as a syntax-object. However, a
|
||||
fully expanded program fits a specific grammar.
|
||||
|
||||
@schemegrammar*[
|
||||
#:literals (#%expression module #%plain-module-begin begin provide
|
||||
define-values define-syntaxes define-values-for-syntax
|
||||
require require-for-syntax require-for-template
|
||||
#%plain-lambda case-lambda if begin begin0 let-values letrec-values
|
||||
set! quote-syntax quote with-continuation-mark
|
||||
#%plain-app #%datum #%top #%variable-reference)
|
||||
[top-level-form general-top-level-form
|
||||
(#%expression expr)
|
||||
(module id name-id
|
||||
(#%plain-module-begin
|
||||
module-level-form ...))
|
||||
(begin top-level-form ...)]
|
||||
[module-level-form general-top-level-form
|
||||
(provide provide-spec ...)]
|
||||
[general-top-level-form expr
|
||||
(define-values (id ...) expr)
|
||||
(define-syntaxes (id ...) expr)
|
||||
(define-values-for-syntax (id ...) expr)
|
||||
(require require-spec ...)
|
||||
(require-for-syntax require-spec ...)
|
||||
(require-for-template require-spec ...)]
|
||||
[expr id
|
||||
(#%plain-lambda formals expr ...+)
|
||||
(case-lambda (formals expr ...+) ...)
|
||||
(if expr expr)
|
||||
(if expr expr expr)
|
||||
(begin expr ...+)
|
||||
(begin0 expr expr ...)
|
||||
(let-values (((id ...) expr) ...)
|
||||
expr ...+)
|
||||
(letrec-values (((id ...) expr) ...)
|
||||
expr ...+)
|
||||
(set! id expr)
|
||||
(#, @scheme[quote] datum)
|
||||
(quote-syntax datum)
|
||||
(with-continuation-mark expr expr expr)
|
||||
(#%plain-app expr ...+)
|
||||
(#%datum . datum)
|
||||
(#%top . id)
|
||||
(#%variable-reference id)
|
||||
(#%variable-reference (#%top . id))]
|
||||
[formals (id ...)
|
||||
(id ...+ . id)
|
||||
id]]
|
||||
@title{Macros}
|
||||
|
|
|
@ -23,9 +23,11 @@
|
|||
@define[(*state c e) (make-element #f (list langle c comma e rangle))]
|
||||
@define-syntax[state (syntax-rules ()
|
||||
[(_ a b) (*state (scheme a) (scheme b))])]
|
||||
@define[(frame n) (make-element "schemevariable"
|
||||
(list "C" (make-element 'subscript (list (format "~a" n)))))]
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@title{Language Model}
|
||||
@title{Evaluation Model}
|
||||
|
||||
Scheme evaluation can be viewed as the simplification of expressions
|
||||
to obtain values. For example, just as an elementary-school student
|
||||
|
@ -41,12 +43,12 @@ Scheme evaluation simplifies
|
|||
|
||||
The arrow @reduces above replaces the more traditional @tt{=} to
|
||||
emphasize that evaluation proceeds in a particular direction towards
|
||||
simpler expressions. In particular, a @defterm{value} is an
|
||||
simpler expressions. In particular, a @deftech{value} is an
|
||||
expression that evaluation simplifies no further, such as the number
|
||||
@scheme[2].
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Sub-expression Evaluation}
|
||||
@section{Sub-expression Evaluation and Continuations}
|
||||
|
||||
Some simplifications require more than one step. For example:
|
||||
|
||||
|
@ -54,14 +56,14 @@ Some simplifications require more than one step. For example:
|
|||
(- 4 #,(redex (+ 1 1))) #,reduces #,(redex (- 4 2)) #,reduces 2
|
||||
]
|
||||
|
||||
An expression that is not a value can always be partitioned into two
|
||||
parts: a @defterm{redex}, which is the part that changed in a
|
||||
An expression that is not a @tech{value} can always be partitioned
|
||||
into two parts: a @deftech{redex}, which is the part that changed in a
|
||||
single-step simplification (show in blue), and the
|
||||
@defterm{continuation}, which is the surrounding expression
|
||||
@deftech{continuation}, which is the surrounding expression
|
||||
context. In @scheme[(- 4 (+ 1 1))], the redex is @scheme[(+ 1 1)], and
|
||||
the continuation is @scheme[(- 4 #, @hole)], where @hole takes the
|
||||
place of the redex. That is, the continuation says how to ``continue''
|
||||
after the redex is reduced to a value.
|
||||
after the @tech{redex} is reduced to a @tech{value}.
|
||||
|
||||
Before some things can be evaluated, some sub-expressions must be
|
||||
evaluated; for example, in the application @scheme[(- 4 (+ 1 1))], the
|
||||
|
@ -72,69 +74,72 @@ Thus, the specification of each syntactic form specifies how (some of)
|
|||
its sub-expressions are evaluated, and then how the results are
|
||||
combined to reduce the form away.
|
||||
|
||||
The @defterm{dynamic extent} of an expression is the sequence of
|
||||
evaluation steps during which an expression contains the redex.
|
||||
The @deftech{dynamic extent} of an expression is the sequence of
|
||||
evaluation steps during which an expression contains the @tech{redex}.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Tail Position}
|
||||
|
||||
An expression @scheme[_expr1] is in @defterm{tail position} with
|
||||
An expression @scheme[_expr1] is in @deftech{tail position} with
|
||||
respect to an enclosing expression @scheme[_expr2] if, whenever
|
||||
@scheme[_expr1] becomes a redex, its continuation is the same as was
|
||||
the enclosing @scheme[_expr2]'s continuation.
|
||||
@scheme[_expr1] becomes a redex, its @tech{continuation} is the same
|
||||
as was the enclosing @scheme[_expr2]'s @tech{continuation}.
|
||||
|
||||
For example, the @scheme[(+ 1 1)] expression is @italic{not} in tail
|
||||
position with respect to @scheme[(- 4 (+ 1 1))]. To illustrate, we use
|
||||
For example, the @scheme[(+ 1 1)] expression is @italic{not} in @tech{tail
|
||||
position} with respect to @scheme[(- 4 (+ 1 1))]. To illustrate, we use
|
||||
the notation @sub[_C _expr] to mean the expression that is produced by
|
||||
substituing @scheme[_expr] in place of @hole in the continuation
|
||||
substituting @scheme[_expr] in place of @hole in the @tech{continuation}
|
||||
@scheme[_C]:
|
||||
|
||||
@schemeblock[
|
||||
#, @sub[_C (- 4 (+ 1 1))] #, @reduces #, @sub[_C (- 4 2)]
|
||||
]
|
||||
|
||||
In this case, the continuation for reducing @scheme[(+ 1 1)] is @sub[_C (+
|
||||
4 #, @hole)], not just @scheme[_C].
|
||||
In this case, the @tech{continuation} for reducing @scheme[(+ 1 1)] is
|
||||
@sub[_C (+ 4 #, @hole)], not just @scheme[_C].
|
||||
|
||||
In contrast, @scheme[(+ 1 1)] is in tail position with respect to
|
||||
@scheme[(if (zero? 0) (+ 1 1) 3)], because, for any continuation @scheme[_C],
|
||||
In contrast, @scheme[(+ 1 1)] is in @tech{tail position} with respect
|
||||
to @scheme[(if (zero? 0) (+ 1 1) 3)], because, for any continuation
|
||||
@scheme[_C],
|
||||
|
||||
@schemeblock[
|
||||
#, @sub[_C (if (zero? 0) (+ 1 1) 3)] #, @reduces #, @sub[_C (if #t (+ 1 1) 3)] #, @reduces #, @sub[_C (+ 1 1)]
|
||||
]
|
||||
|
||||
The steps in this reduction sequence are driven by the definition of
|
||||
@scheme[if], and they do not depend on the continuation
|
||||
@scheme[if], and they do not depend on the @tech{continuation}
|
||||
@scheme[_C]. The ``then'' branch of an @scheme[if] form is always in
|
||||
tail position with respect to the @scheme[if] form. Due to a similar
|
||||
reduction rule for @scheme[if] and @scheme[#f], the ``else'' branch of
|
||||
an @scheme[if] form is also in tail position.
|
||||
@tech{tail position} with respect to the @scheme[if] form. Due to a
|
||||
similar reduction rule for @scheme[if] and @scheme[#f], the ``else''
|
||||
branch of an @scheme[if] form is also in @tech{tail position}.
|
||||
|
||||
Tail-position specifications provide a guarantee about the asymtotic
|
||||
space consumption of a computation. In general, the specification of
|
||||
tail positions goes with each syntactic form, like @scheme[if].
|
||||
@tech{Tail-position} specifications provide a guarantee about the
|
||||
asymptotic space consumption of a computation. In general, the
|
||||
specification of @tech{tail positions} goes with each syntactic form,
|
||||
like @scheme[if].
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Multiple Return Values}
|
||||
|
||||
A Scheme expression can evaluate to @defterm{multiple values}, in the
|
||||
A Scheme expression can evaluate to @deftech{multiple values}, in the
|
||||
same way that a procedure can accept multiple arguments.
|
||||
|
||||
Most continuations expect a particular number of result values.
|
||||
Indeed, most continuations, such as @scheme[(+ #, @hole 1)] expect a
|
||||
single value. The continuation @scheme[(let-values ([(x y) #, @hole])
|
||||
_expr)] expects two result values; the first result replaces
|
||||
@scheme[x] in the body @scheme[_expr], and the second replaces
|
||||
@scheme[y] in @scheme[_expr]. The continuation @scheme[(begin #, @hole
|
||||
(+ 1 2))] accepts any number of result values, because it ignores the
|
||||
result(s).
|
||||
Most @tech{continuations} expect a particular number of result
|
||||
@tech{values}. Indeed, most @tech{continuations}, such as @scheme[(+
|
||||
#, @hole 1)] expect a single @tech{value}. The @tech{continuation}
|
||||
@scheme[(let-values ([(x y) #, @hole]) _expr)] expects two result
|
||||
@tech{values}; the first result replaces @scheme[x] in the body
|
||||
@scheme[_expr], and the second replaces @scheme[y] in
|
||||
@scheme[_expr]. The @tech{continuation} @scheme[(begin #, @hole (+ 1
|
||||
2))] accepts any number of result @tech{values}, because it ignores
|
||||
the result(s).
|
||||
|
||||
In general, the specification of a syntactic form inidicates the
|
||||
number of values that it produces and the number that it expects from
|
||||
each of its sub-expression. In addtion, some procedures (notably
|
||||
@scheme[values]) produce multiple values, and some procedures (notably
|
||||
@scheme[call-with-values]) create continuations internally that accept
|
||||
a certain number of values.
|
||||
number of @tech{values} that it produces and the number that it
|
||||
expects from each of its sub-expression. In addtion, some procedures
|
||||
(notably @scheme[values]) produce multiple @tech{values}, and some
|
||||
procedures (notably @scheme[call-with-values]) create continuations
|
||||
internally that accept a certain number of @tech{values}.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Top-Level and Module-Level Variables}
|
||||
|
@ -147,9 +152,9 @@ then an algebra student simplifies @tt{x + 1} as follows:
|
|||
|
||||
@verbatim{ x + 1 = 10 + 1 = 11}
|
||||
|
||||
Scheme works much the same way, in that a set of top-level variables
|
||||
are available for substitutions on demand during evaluation. For
|
||||
example, given
|
||||
Scheme works much the same way, in that a set of @tech{top-level
|
||||
variables} are available for substitutions on demand during
|
||||
evaluation. For example, given
|
||||
|
||||
@schemeblock[
|
||||
(define x 10)
|
||||
|
@ -169,7 +174,7 @@ definitions in response to evaluating forms such as @scheme[define].
|
|||
Each evaluation step, then, takes the current set of definitions and
|
||||
program to a new set of definitions and program. Before a
|
||||
@scheme[define] can be moved into the set of definitions, its
|
||||
right-hand expression must be reduced to a value.
|
||||
right-hand expression must be reduced to a @tech{value}.
|
||||
|
||||
@prog-steps/no-obj[
|
||||
[{}
|
||||
|
@ -191,7 +196,7 @@ a module is essentially a prefix on a defined name, so that different
|
|||
modules can define the name.
|
||||
|
||||
Using @scheme[set!], a program can change the value associated with an
|
||||
existing top-level variable:
|
||||
existing @tech{top-level variable}:
|
||||
|
||||
@prog-steps/no-obj[
|
||||
[{(define x 10)}
|
||||
|
@ -207,26 +212,27 @@ existing top-level variable:
|
|||
@;------------------------------------------------------------------------
|
||||
@section{Objects and Imperative Update}
|
||||
|
||||
In addition to @scheme[set!] for imperative update of top-level
|
||||
variables, various procedures enable the modification of elements
|
||||
In addition to @scheme[set!] for imperative update of @tech{top-level
|
||||
variables}, various procedures enable the modification of elements
|
||||
within a compound data structure. For example, @scheme[vector-set!]
|
||||
modifies the content of a vector.
|
||||
|
||||
To allow such modifications to data, we must distingiush between
|
||||
values, which are the results of expressions, and @defterm{objects},
|
||||
which hold the data referenced by a value.
|
||||
@tech{values}, which are the results of expressions, and
|
||||
@deftech{objects}, which hold the data referenced by a value.
|
||||
|
||||
A few kinds of objects can serve directly as values, including
|
||||
A few kinds of @tech{objects} can serve directly as values, including
|
||||
booleans, @scheme[(void)], and small exact integers. More generally,
|
||||
however, a value is a reference to an object. For example, a value can
|
||||
be a reference to a particular vector that currently holds the value
|
||||
@scheme[10] in its first slot. If an object is modified, then the
|
||||
modification is visible through all copies of the value that reference
|
||||
the same object.
|
||||
however, a @tech{value} is a reference to an @tech{object}. For
|
||||
example, a @tech{value} can be a reference to a particular vector that
|
||||
currently holds the value @scheme[10] in its first slot. If an
|
||||
@tech{object} is modified, then the modification is visible through
|
||||
all copies of the @tech{value} that reference the same @tech{object}.
|
||||
|
||||
In the evaluation model, a set of objects must be carried along with
|
||||
each step in evaluation, just like the definition set. Operations that
|
||||
create objects, such as @scheme[vector], add to the set of objects:
|
||||
In the evaluation model, a set of @tech{objects} must be carried along
|
||||
with each step in evaluation, just like the definition set. Operations
|
||||
that create @tech{objects}, such as @scheme[vector], add to the set of
|
||||
@tech{objects}:
|
||||
|
||||
@prog-steps[
|
||||
[{}
|
||||
|
@ -292,38 +298,39 @@ create objects, such as @scheme[vector], add to the set of objects:
|
|||
11]
|
||||
]
|
||||
|
||||
The distinction between a top-level variable is an object reference is
|
||||
crucial. A top-level variable is not a value; each time a variable
|
||||
expression is evaluated, the value is extracted from the current set
|
||||
of definitions. An object reference, in contrast is a value, and
|
||||
therefore needs no further evaluation. The model evaluation steps
|
||||
above use angle-bracketed @scheme[<o1>] for an object reference to
|
||||
distinguish it from a variable name.
|
||||
The distinction between a @tech{top-level variable} and an object
|
||||
reference is crucial. A @tech{top-level variable} is not a
|
||||
@tech{value}; each time a @tech{variable} expression is evaluated, the
|
||||
value is extracted from the current set of definitions. An object
|
||||
reference, in contrast is a value, and therefore needs no further
|
||||
evaluation. The model evaluation steps above use angle-bracketed
|
||||
@scheme[<o1>] for an object reference to distinguish it from a
|
||||
@tech{variable} name.
|
||||
|
||||
A direct object reference can never appear in a text-based source
|
||||
program. A program representation created with
|
||||
@scheme[datum->syntax-object], however, can embed direct references to
|
||||
existing objects.
|
||||
existing @tech{objects}.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Object Identity and Comparisons}
|
||||
|
||||
The @scheme[eq?] operator compares two values, returning @scheme[#t]
|
||||
when the values refer to the same object. This form of equality is
|
||||
suitable for comparing objects that support imperative update (e.g.,
|
||||
to determine that the effect of modifying an object through one
|
||||
reference is visible through another reference). Also, an @scheme[eq?]
|
||||
test evaluates quickly, and @scheme[eq?]-based hashing is more
|
||||
lightweight than @scheme[equal?]-based hashing in hash tables.
|
||||
The @scheme[eq?] operator compares two @tech{values}, returning
|
||||
@scheme[#t] when the values refer to the same @tech{object}. This form
|
||||
of equality is suitable for comparing objects that support imperative
|
||||
update (e.g., to determine that the effect of modifying an object
|
||||
through one reference is visible through another reference). Also, an
|
||||
@scheme[eq?] test evaluates quickly, and @scheme[eq?]-based hashing
|
||||
is more lightweight than @scheme[equal?]-based hashing in hash tables.
|
||||
|
||||
In some cases, however, @scheme[eq?] is unsuitable as a comparison
|
||||
operator, because the generation of objects is not clearly defined. In
|
||||
particular, two applications of @scheme[+] to the same two exact
|
||||
integers may or may not produce results that are @scheme[eq?],
|
||||
operator, because the generation of @tech{objects} is not clearly
|
||||
defined. In particular, two applications of @scheme[+] to the same two
|
||||
exact integers may or may not produce results that are @scheme[eq?],
|
||||
although the results are always @scheme[equal?]. Similarly, evaluation
|
||||
of a @scheme[lambda] form typically generates a new procedure object,
|
||||
but it may re-use a procedure object previously generated by the same
|
||||
source @scheme[lambda] form.
|
||||
of a @scheme[lambda] form typically generates a new procedure
|
||||
@tech{object}, but it may re-use a procedure @tech{object} previously
|
||||
generated by the same source @scheme[lambda] form.
|
||||
|
||||
The behavior of a datatype with respect to @scheme[eq?] is generally
|
||||
specified with the datatype and its associated procedures.
|
||||
|
@ -342,16 +349,17 @@ In the program state
|
|||
|
||||
evaluation cannot depend on @scheme[<o2>], because it is not part of
|
||||
the program to evaluate, and it is not referenced by any definition
|
||||
that is accessible in the program. The object @scheme[<o2>] may
|
||||
therefore be removed from the evaluation by @defterm{garbage
|
||||
that is accessible in the program. The @tech{object} @scheme[<o2>] may
|
||||
therefore be removed from the evaluation by @deftech{garbage
|
||||
collection}.
|
||||
|
||||
A few special compound datatypes hold @defterm{weak references} to
|
||||
objects. Such weak references are treated specialy by the garbage
|
||||
collector in determining which objects are reachable for the remainder
|
||||
of the computation. If an object is reachable only via a weak
|
||||
reference, then the object can be reclaimed, and the weak reference is
|
||||
replaced by a different value (typically @scheme[#f]).
|
||||
A few special compound datatypes hold @deftech{weak references} to
|
||||
objects. Such weak references are treated specially by the garbage
|
||||
collector in determining which @tech{objects} are reachable for the
|
||||
remainder of the computation. If an @tech{object} is reachable only
|
||||
via a @tech{weak reference}, then the object can be reclaimed, and the
|
||||
@tech{weak reference} is replaced by a different @tech{value}
|
||||
(typically @scheme[#f]).
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Procedure Applications and Local Bindings}
|
||||
|
@ -365,11 +373,12 @@ then an algebra student simplifies @tt{f(1)} as follows:
|
|||
@verbatim{ f(7) = 7 + 10 = 17}
|
||||
|
||||
The key step in this simplification is take the body of the defined
|
||||
function @tt{f}, and then replace each @tt{x} with the actual value
|
||||
@tt{1}.
|
||||
function @tt{f}, and then replace each @tt{x} with the actual
|
||||
@tech{value} @tt{1}.
|
||||
|
||||
Scheme procedure application works much the same way. A procedure is
|
||||
an object, so evaluating @scheme[(f 7)] starts with a variable lookup:
|
||||
an @tech{object}, so evaluating @scheme[(f 7)] starts with a
|
||||
@tech{variable} lookup:
|
||||
|
||||
@prog-steps[
|
||||
[{(define <p1> (lambda (x) (+ x 10)))}
|
||||
|
@ -380,16 +389,17 @@ an object, so evaluating @scheme[(f 7)] starts with a variable lookup:
|
|||
(code:hilite (<p1> 7))]
|
||||
]
|
||||
|
||||
Unlike in algebra, however, the value associated with an argument can
|
||||
be changed in the body of a procedure by using @scheme[set!], as in
|
||||
the example @scheme[(lambda (x) (begin (set! x 3) x))]. Since the value
|
||||
associated with @scheme[x] can be changed, an actual value for cannot
|
||||
be substituted for @scheme[x] when the procedure is applied.
|
||||
Unlike in algebra, however, the @tech{value} associated with an
|
||||
argument can be changed in the body of a procedure by using
|
||||
@scheme[set!], as in the example @scheme[(lambda (x) (begin (set! x 3)
|
||||
x))]. Since the @tech{value} associated with @scheme[x] can be
|
||||
changed, an actual value for cannot be substituted for @scheme[x] when
|
||||
the procedure is applied.
|
||||
|
||||
Instead, a new @defterm{location} is created for each variable on each
|
||||
application. The argument value is placed in the location, and each
|
||||
insteace of the variable in the procedure body is replaced with the
|
||||
new location:
|
||||
Instead, a new @deftech{location} is created for each @tech{variable}
|
||||
on each application. The argument @tech{value} is placed in the
|
||||
@tech{location}, and each instance of the @tech{variable} in the
|
||||
procedure body is replaced with the new @tech{location}:
|
||||
|
||||
@prog-steps[
|
||||
[{(define <p1> (lambda (x) (+ x 10)))}
|
||||
|
@ -409,14 +419,16 @@ new location:
|
|||
17]
|
||||
]
|
||||
|
||||
A location is the same as a top-level variable, but when a location is
|
||||
generated, it (conceptually) uses a name that has not been used before
|
||||
and that cannot not be generated again or accessed directly.
|
||||
A @tech{location} is the same as a @tech{top-level variable}, but when
|
||||
a @tech{location} is generated, it (conceptually) uses a name that has
|
||||
not been used before and that cannot not be generated again or
|
||||
accessed directly.
|
||||
|
||||
Generating a location in this way means that @scheme[set!] evaluates
|
||||
for local variables in the same way as for top-level variables,
|
||||
because the local variable is always replaced with a location by the
|
||||
time the @scheme[set!] form is evaluated:
|
||||
Generating a @tech{location} in this way means that @scheme[set!]
|
||||
evaluates for @tech{local variables} in the same way as for
|
||||
@tech{top-level variables}, because the @tech{local variable} is
|
||||
always replaced with a @tech{location} by the time the @scheme[set!]
|
||||
form is evaluated:
|
||||
|
||||
@prog-steps[
|
||||
[{(define <p1> (lambda (x) (begin (set! x 3) x)))}
|
||||
|
@ -443,138 +455,85 @@ time the @scheme[set!] form is evaluated:
|
|||
3]
|
||||
]
|
||||
|
||||
The substition and location-generation step of procedure application
|
||||
requires that the argument is a value. Therefore, in @scheme[((lambda
|
||||
(x) (+ x 10)) (+ 1 2))], the @scheme[(+ 1 2)] sub-expression must be
|
||||
simplified to the value @scheme[3], and then @scheme[3] can be placed
|
||||
into a location for @scheme[x]. In other words, Scheme is a
|
||||
@defterm{call-by-value} language.
|
||||
The substition and @tech{location}-generation step of procedure
|
||||
application requires that the argument is a @tech{value}. Therefore,
|
||||
in @scheme[((lambda (x) (+ x 10)) (+ 1 2))], the @scheme[(+ 1 2)]
|
||||
sub-expression must be simplified to the @tech{value} @scheme[3], and
|
||||
then @scheme[3] can be placed into a @tech{location} for
|
||||
@scheme[x]. In other words, Scheme is a @deftech{call-by-value}
|
||||
language.
|
||||
|
||||
Evaluation of a local binding, such as @scheme[(let ([x (+ 1 2)])
|
||||
_expr)], is the same as for a procedure call. After @scheme[(+ 1 2)]
|
||||
produces a value, it is stored in a fresh location that replaces every
|
||||
instance of @scheme[x] in @scheme[_expr].
|
||||
produces a @tech{value}, it is stored in a fresh @tech{location} that
|
||||
replaces every instance of @scheme[x] in @scheme[_expr].
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Identifiers, Variables, and Locations}
|
||||
@section{Variables and Locations}
|
||||
|
||||
A @defterm{variable} is a placeholder for a value, and an expressions
|
||||
in an initial program refer to variables. A top-level variable is both
|
||||
a variable and a location. Any other variable is always replaced by a
|
||||
location at run-time, so that evaluation of expressions involves only
|
||||
locations. A single non-top-level variable, such as a procedure
|
||||
argument, can correspond to different locations at different times.
|
||||
A @deftech{variable} is a placeholder for a @tech{value}, and an
|
||||
expressions in an initial program refer to @tech{variables}. A
|
||||
@deftech{top-level variable} is both a @tech{variable} and a
|
||||
@tech{location}. Any other @tech{variable} is always replaced by a
|
||||
@tech{location} at run-time, so that evaluation of expressions
|
||||
involves only @tech{locations}. A single @deftech{local variable}
|
||||
(i.e., a non-top-level, non-module-level @tech{variable}), such as a
|
||||
procedure argument, can correspond to different @tech{locations}
|
||||
through different instantiations.
|
||||
|
||||
The replacement of a variable with a location during evaluation
|
||||
implements Scheme's @defterm{lexical scoping}. For example, when the
|
||||
procedure-argument variable @scheme[x] is replaced by the location
|
||||
@scheme[xloc], then it is replaced throughout the body of the
|
||||
procedure, including with any nested @scheme[lambda] forms. As a
|
||||
result, future references of the variable always access the same
|
||||
location.
|
||||
For example, in the program
|
||||
|
||||
@guideintro["guide:binding"]{binding}
|
||||
@schemeblock[(define y (+ (let ([x 5]) x) 6))]
|
||||
|
||||
An @defterm{identifier} is source-program entity. Parsing a Scheme
|
||||
program reveals that some identifiers correspond to variables, some
|
||||
refer to syntactic forms, and some are quoted to produce a symbol or a
|
||||
syntax object. An identifier @defterm{binds} another when the former is
|
||||
parsed as a variable and the latter is parsed as a reference to the
|
||||
former. An identifier is @defterm{bound} in a sub-expression if it
|
||||
binds any uses of the identifier in the sub-expression that are not
|
||||
otherwise bound within the sub-expression; conversely, a binding for a
|
||||
sub-expression @defterm{shadows} any bindings in its context, so that
|
||||
uses of an identifier refer to the shaodowing binding.
|
||||
both @scheme[y] and @scheme[x] are @tech{variables}. The @scheme[y]
|
||||
@tech{variable} is a @tech{top-level variable}, and the @scheme[x] is
|
||||
a @tech{local variable}. When this code is evaluated, a
|
||||
@tech{location} is created for @scheme[x] to hold the value
|
||||
@scheme[5], and a @tech{location} is also created for @scheme[y] to
|
||||
hold the value @scheme[6].
|
||||
|
||||
Throughout the documentation, identifiers are typeset to suggest the
|
||||
way that they are parsed. A black, boldface identifier like
|
||||
@scheme[lambda] indicates as a reference to a syntactic form. A plain
|
||||
blue identifer like @schemeidfont{x} is a variable or a reference to
|
||||
an unspecified top-level definition. A hyperlinked identifier
|
||||
@scheme[cons] is a reference to a specific top-level definition.
|
||||
The replacement of a @tech{variable} with a @tech{location} during
|
||||
evaluation implements Scheme's @deftech{lexical scoping}. For example,
|
||||
when a procedure-argument @tech{variable} @scheme[x] is replaced by
|
||||
the @tech{location} @scheme[xloc], then it is replaced throughout the
|
||||
body of the procedure, including with any nested @scheme[lambda]
|
||||
forms. As a result, future references of the @tech{variable} always
|
||||
access the same @tech{location}.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Parsing and Compilation}
|
||||
@section{Continuation Frames and Marks}
|
||||
|
||||
The syntax of a Scheme program is defined by
|
||||
Every continuation @scheme[_C] can be partitioned into
|
||||
@deftech{continuation frames} @frame[1], @frame[2], ..., @frame["n"]
|
||||
such that @scheme[_C] = @*sub[@frame[1] @*sub[@frame[2] @*sub["..."
|
||||
@frame["n"]]]], and no frame @frame["i"] can be itself partitioned
|
||||
into smaller continuations. Evaluation steps add an remove frames to
|
||||
the current continuation, typically one at a time.
|
||||
|
||||
@itemize{
|
||||
|
||||
@item{a @defterm{read} phase that processes a character stream into a
|
||||
Scheme value, especially one composed of pairs and symbols,
|
||||
and}
|
||||
|
||||
@item{an @defterm{expand} phase that processes the value to finish
|
||||
parsing the code.}
|
||||
|
||||
}
|
||||
|
||||
For details on the read phase, see @secref["mz:reader"]. Source code
|
||||
is normally read in @scheme[read-syntax] mode, otherwise it must be
|
||||
converted to syntax using @scheme[datum->syntax]; the expand phase is
|
||||
defined in terms of syntax objects.
|
||||
|
||||
An identifier, for example, is a syntax-object symbol, and the
|
||||
identifier's binding is determined by lexical information attached to
|
||||
the identifier. Expansion recursively processes a syntax object,
|
||||
both using its lexical information and extending the information for
|
||||
nested objects. For details, see @secref["mz:expansion"].
|
||||
|
||||
Ultimately, expansion produces a syntax object matching the grammar
|
||||
of the forms in @secref["mz:fully-expanded"]. This fully-expanded
|
||||
datum corresponds to a parsed expression, and lexical information on
|
||||
its identifiers indicates the parse. For example, a @scheme[car]
|
||||
identifier might have lexical information that designates it as the
|
||||
@scheme[car] from the @schememodname[big] language (i.e., the built-in
|
||||
@scheme[car]). Similarly, a @scheme[lambda] identifier's lexical
|
||||
information may indicate that it represents a procedure form.
|
||||
Each frame is conceptually annotated with a set of
|
||||
@deftech{continuation marks}. A mark consists of a key and its value;
|
||||
the key is an arbitrary value, and each frame includes at most one
|
||||
mark for any key. Various operations set and extract marks from
|
||||
continuations, so that marks can be used to attach information to a
|
||||
dynamic extent. For example, marks can be used to record information
|
||||
for a ``stack trace'' to be used when an exception is raised, or
|
||||
implement dynamically scoped bindings.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Namespaces}
|
||||
@section{Prompts and Delimited Continuations}
|
||||
|
||||
A @idefterm{namespace} is a top-level mapping from symbols to binding
|
||||
information. It is the starting point for expanding an expression; a
|
||||
syntax object produced by @scheme[read-syntax] has no initial
|
||||
lexical context; the syntax object can be expanded after
|
||||
initializing it with the mappings of a particular namespace.
|
||||
|
||||
A namespace maps each symbol to one of three possible bindings:
|
||||
|
||||
@itemize{
|
||||
|
||||
@item{a particular module-level binding from a particular module}
|
||||
|
||||
@item{a top-level transformer binding named by the symbol}
|
||||
|
||||
@item{a top-level variable named by the symbol}
|
||||
|
||||
}
|
||||
|
||||
An ``empty'' namespace maps all symbols to top-level
|
||||
variables. Importing a module into the top-level adjusts the namespace
|
||||
bindings for all of the imported named. Evaluating a top-level
|
||||
@scheme[define] form updates the namespace's mapping to refer to a
|
||||
variable (if it does not already) and installs a value into the
|
||||
variable.
|
||||
|
||||
In addition to its main mapping, each namespace encapsulates a
|
||||
distinct set of top-level variables, as well as a potentially distinct
|
||||
set of module instances. After a namespace is created, module
|
||||
instances from existing namespaces can be attached to the new
|
||||
namespace.
|
||||
|
||||
At all times during evaluation, some namespace is designated as the
|
||||
@defterm{current namespace}. The current namespace has no particular
|
||||
relationship, however, with the namespace used to expand the code that
|
||||
is executing. Furthermore, a namespace is purely a top-level concept;
|
||||
it does not encapsulate the full environment of an expression within
|
||||
local binding forms.
|
||||
|
||||
In terms of the evaluation model, top-level variables from different
|
||||
namespaces essentially correspond to definitions with different
|
||||
prefixes. In particular, changing the current namespace during
|
||||
evaluation does not change the variables to which executing
|
||||
expressions refer.
|
||||
A @deftech{prompt} is a special kind of continuation frame that is
|
||||
annotated with a specific @deftech{prompt-tag} (essentially a
|
||||
continuation mark). Various operations allow the capture of frames in
|
||||
the continuation from the redex position out to the nearest enclosing
|
||||
prompt with a particular prompt tag; such a continuation is sometimes
|
||||
called a @deftech{delimited continuation}. Other operations allow the
|
||||
current continuation to be extended with a captured continuation
|
||||
(specifically, a @deftech{composable continuation}). Yet other
|
||||
operations abort the computation to the nearest enclosing prompt with
|
||||
a particular tag, or replace the continuation to the nearest enclosing
|
||||
prompt with another one. When a delimited continuation is captured,
|
||||
the marks associated with the relevant frames are also captured.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Threads}
|
||||
|
@ -588,8 +547,64 @@ state. Most evaluation steps involve a single step in a single
|
|||
expression, but certain synchronization primitives require multiple
|
||||
threads to progress together in one step.
|
||||
|
||||
In addition to shared state, each thread has its own private state
|
||||
that is accessed through @defterm{thread cells} and
|
||||
@defterm{parameters}. In particular, the current namespace is a
|
||||
thread-specific property implemented by a parameter; it is not a
|
||||
global property.
|
||||
In addition to the state that is shared among all threads, each thread
|
||||
has its own private state that is accessed through @deftech{thread
|
||||
cells}. A thread cell is similar to a normal mutable object, but a
|
||||
change to the value inside a thread cell is seen only when extracting
|
||||
a value from the cell from the same thread. A thread cell can be
|
||||
@deftech{preserved}; when a new thread is created, the creating
|
||||
thread's value for a preserved thread cell serves as the initial value
|
||||
for the cell in the created thread. For a non-preserved thread cell, a
|
||||
new thread sees the same initial value (specified when the thread cell
|
||||
is created) as all other threads.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Parameters}
|
||||
|
||||
A @deftech{parameter} is essentially a derived concept in Scheme; they
|
||||
are defined in terms of continuation marks and thread cells. However,
|
||||
parameters are also built in, in the sense that some primitive
|
||||
procedures consult parameter values. For example, the default output
|
||||
stream for primitive output operations is determined by a parameter.
|
||||
|
||||
A parameter is a setting that is both thread-specific and
|
||||
continuation-specific. In the empty continuation, each parameter
|
||||
corresponds to a preserved thread cell; a corresponding
|
||||
@deftech{parameter procedure} accesses and sets the thread cell's
|
||||
value for the current thread.
|
||||
|
||||
In a non-empty continuation, a parameter's value is determined through
|
||||
a @deftech{parameterization} that is associated with the nearest
|
||||
enclosing continuation frame though a continuation mark (whose key is
|
||||
not directly accessible). A parameterization maps each parameter to a
|
||||
preserved thread cell, and the combination of thread cell and current
|
||||
thread yields the parameter's value. A parameter procedure sets or
|
||||
accesses the relevant thread cell for its parameter.
|
||||
|
||||
Various operations, such as @scheme[parameterize] or
|
||||
@scheme[with-parameterization], install a parameterization into the
|
||||
current continuation's frame.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Exceptions}
|
||||
|
||||
@deftech{Exceptions} are essentially a derived concept in Scheme; they
|
||||
are defined in terms of continuations, prompts, and continuation
|
||||
marks. However, exceptions are also built in, in the sense that
|
||||
primitive forms and procedures may raise exceptions.
|
||||
|
||||
A handler for uncaught exceptions is designated through a built-in
|
||||
parameter. A handler to catch exceptions can be associated with a
|
||||
continuation frame though a continuation mark (whose key is not
|
||||
directly accessible). When an exception is raised, the current
|
||||
continuation's marks determine a chain of handler procedures that are
|
||||
consulted to handle the exception.
|
||||
|
||||
One potential action of an exception handler is to abort the current
|
||||
continuation up to an enclosing prompt with a particular tag. The
|
||||
default handler for uncaught exceptions, in particular, aborts to a
|
||||
particular tag for which a prompt is always present, because the
|
||||
prompt is installed in the outermost frame of the continuation for any
|
||||
new thread.
|
||||
|
||||
|
||||
|
|
|
@ -22,19 +22,20 @@ through a @seclink["mz:readtables"]{readtable} and various other
|
|||
@seclink["parameters"]{parameters}. This section describes the reader's
|
||||
parsing when using the default readtable.
|
||||
|
||||
Reading from a stream produces one datum. If the result datum is a
|
||||
compound value, then reading the datum typically requires the reader
|
||||
to call itself recursively to read the component data.
|
||||
Reading from a stream produces one @defterm{datum}. If the result
|
||||
datum is a compound value, then reading the datum typically requires
|
||||
the reader to call itself recursively to read the component data.
|
||||
|
||||
The reader can be invoked in either of two modes: @scheme[read] mode,
|
||||
or @scheme[read-syntax] mode. In @scheme[read-syntax] mode, the result
|
||||
is always a @seclink["stxobj"]{syntax object} that includes
|
||||
source-location information wrapped around the sort of datum that
|
||||
@scheme[read] mode would produce. In the case of pairs, vectors, and
|
||||
boxes, morever, the content is also wrapped recursively as a syntax
|
||||
object. Unless specified otherwise, this section describes the
|
||||
reader's behavior in @scheme[read] mode, and @scheme[read-syntax] mode
|
||||
does the same modulo wrapping the final result.
|
||||
source-location and (initially empty) lexical information wrapped
|
||||
around the sort of datum that @scheme[read] mode would produce. In the
|
||||
case of pairs, vectors, and boxes, morever, the content is also
|
||||
wrapped recursively as a syntax object. Unless specified otherwise,
|
||||
this section describes the reader's behavior in @scheme[read] mode,
|
||||
and @scheme[read-syntax] mode does the same modulo wrapping the final
|
||||
result.
|
||||
|
||||
Reading is defined in terms of Unicode characters; see
|
||||
@secref["mz:char-input"] for information on how a byte stream is converted
|
||||
|
|
|
@ -12,8 +12,8 @@ language.
|
|||
@table-of-contents[]
|
||||
|
||||
@include-section["model.scrbl"]
|
||||
@include-section["syntax-model.scrbl"]
|
||||
@include-section["read.scrbl"]
|
||||
@include-section["macros.scrbl"]
|
||||
@include-section["syntax.scrbl"]
|
||||
@include-section["derived.scrbl"]
|
||||
@include-section["data.scrbl"]
|
||||
|
@ -22,7 +22,7 @@ language.
|
|||
|
||||
@section["Input and Output"]
|
||||
|
||||
@subsection[#:tag "mz:char-input"]{Form Bytes to Characters}
|
||||
@subsection[#:tag "mz:char-input"]{From Bytes to Characters}
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
|
||||
|
|
423
collects/scribblings/reference/syntax-model.scrbl
Normal file
423
collects/scribblings/reference/syntax-model.scrbl
Normal file
|
@ -0,0 +1,423 @@
|
|||
#reader(lib "docreader.ss" "scribble")
|
||||
@require[(lib "struct.ss" "scribble")]
|
||||
@require-for-syntax[mzscheme]
|
||||
@require["mz.ss"]
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@title{Syntax Model}
|
||||
|
||||
The syntax of a Scheme program is defined by
|
||||
|
||||
@itemize{
|
||||
|
||||
@item{a @deftech{read} phase that processes a character stream into a
|
||||
Scheme value, especially one composed of pairs and symbols, and
|
||||
possibly with source-location information to form a
|
||||
@tech{syntax object}; and}
|
||||
|
||||
@item{an @deftech{expand} phase that processes a syntax object to
|
||||
produce one that is fully parsed.}
|
||||
|
||||
}
|
||||
|
||||
For details on the @tech{read} phase, see @secref["mz:reader"]. Source
|
||||
code is normally read in @scheme[read-syntax] mode. Otherwise, it must
|
||||
be converted to syntax using @scheme[datum->syntax], because the
|
||||
@tech{expand} phase is defined in terms of @tech{syntax objects}.
|
||||
|
||||
Expansion recursively processes a @tech{syntax object}. Binding
|
||||
information on a syntax object drives the expansion process, and the
|
||||
expansion process generates new syntax objects that are like old ones,
|
||||
but enriched with new binding information.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Identifiers and Binding}
|
||||
|
||||
@guideintro["guide:binding"]{binding}
|
||||
|
||||
An @deftech{identifier} is source-program entity. Parsing a Scheme
|
||||
program reveals that some @tech{identifiers} correspond to
|
||||
@tech{variables}, some refer to syntactic forms, and some are quoted
|
||||
to produce a symbol or a syntax object. An identifier @deftech{binds}
|
||||
another (i.e., it is a @deftech{binding}) when the former is parsed as
|
||||
a @tech{variable} and the latter is parsed as a reference to the
|
||||
former. An @tech{identifier} is @deftech{bound} in a sub-expression if
|
||||
it @tech{binds} any uses of the @tech{identifier} in the
|
||||
sub-expression that are not otherwise @tech{bound} within the
|
||||
sub-expression; conversely, a binding for a sub-expression
|
||||
@deftech{shadows} any @tech{bindings} (i.e., it is
|
||||
@deftech{shadowing}) in its context, so that uses of an
|
||||
@tech{identifier} refer to the @tech{shadowing} @tech{binding}. The
|
||||
@deftech{environment} of a form is the set of bindings whose scope
|
||||
includes the form.
|
||||
|
||||
For example, as a bit of source, the text
|
||||
|
||||
@schemeblock[(let ([x 5]) x)]
|
||||
|
||||
includes two @tech{identifiers}: @scheme[let] and @scheme[x] (which
|
||||
appears twice). When this source is parsed in a typical
|
||||
@tech{environment}, @scheme[x] turns out to represent a
|
||||
@tech{variable} (unlike @scheme[let]). In particular, the first
|
||||
@scheme[x] @tech{binds} the second @scheme[x].
|
||||
|
||||
Throughout the documentation, @tech{identifiers} are typeset to
|
||||
suggest the way that they are parsed. A black, boldface
|
||||
@tech{identifier} like @scheme[lambda] indicates as a reference to a
|
||||
syntactic form. A plain blue @tech{identifier} like @schemeidfont{x}
|
||||
is a @tech{variable} or a reference to an unspecified @tech{top-level
|
||||
variable}. A hyperlinked @tech{identifier} @scheme[cons] is a
|
||||
reference to a specific @tech{top-level variable}.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Syntax Objects}
|
||||
|
||||
A @deftech{syntax object} combines a simpler Scheme value, such as a
|
||||
symbol or pair, which information about bindings and (optionally)
|
||||
source-location information. In particular, an @tech{identifier} is
|
||||
represented as a symbol object that combines a symbol and
|
||||
lexical/source information.
|
||||
|
||||
For example, a @schemeidfont{car} @tech{identifier} might have lexical
|
||||
information that designates it as the @scheme[car] from the
|
||||
@schememodname[big] language (i.e., the built-in
|
||||
@scheme[car]). Similarly, a @schemeidfont{lambda} identifier's lexical
|
||||
information may indicate that it represents a procedure form. Some
|
||||
other @tech{identifier}'s lexical information may indicate that it
|
||||
references a @tech{top-level variable}.
|
||||
|
||||
When a @tech{syntax object} represents a more complex expression than
|
||||
am identifier or simple constant, its internal pieces can be
|
||||
extracted. Detailed information about binding is available mostly
|
||||
indirectly. For example, two identifiers, perhaps extracted from a
|
||||
larger expression, can be compared to see if they refer to the same
|
||||
binding (i.e., @scheme[free-identifier=?]). A slightly different test
|
||||
is whether each identifier would bind the other if one was in a
|
||||
binding position and the other in an expression position (i.e.,
|
||||
@scheme[bound-identifier=?]).
|
||||
|
||||
For example, the when the program written as
|
||||
|
||||
@schemeblock[(let ([x 5]) (+ x 6))]
|
||||
|
||||
is represented as a syntax object, then two @tech{syntax objects} can
|
||||
be extracted for the two @scheme[x]s. Both the
|
||||
@scheme[free-identifier=?] and @scheme[bound-identifier=?] predicates
|
||||
will indicate that the @scheme[x]s are the same. In contrast, the
|
||||
@scheme[let] identifier is not @scheme[free-identifier=?] or
|
||||
@scheme[bound-identifier=?] to either @scheme[x].
|
||||
|
||||
The lexical information in a syntax object is independent of the other
|
||||
half, and it can be transferred to a new syntax object, combined with
|
||||
an arbitrary other Scheme value. Thus, identifier-binding information
|
||||
in a syntax object is predicated on the symbolic name of the
|
||||
identifier; the same question with the same lexical information but
|
||||
different base value can produce a different answer.
|
||||
|
||||
For example, combining the lexical information from @scheme[let] in
|
||||
the program above to @scheme['x] would not produce an identifier that
|
||||
is @scheme[free-identifier=?] to either @scheme[x], since it does not
|
||||
appear in the scope of the @scheme[x] binding. Combining the lexical
|
||||
context of the @scheme[6] with @scheme['x], in contrast, would produce
|
||||
an identifier that is @scheme[bound-identifier=?] to both @scheme[x]s.
|
||||
|
||||
The @scheme[quote-syntax] form bridges the evaluation of a program and
|
||||
the representation of a program. Specifically, @scheme[(quote-syntax
|
||||
_datum)] produces a syntax object that preserves all of the lexical
|
||||
information that @scheme[_datum] had when it was parsed as part of a
|
||||
@scheme[quote-syntax] form.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section[#:tag "mz:expansion"]{Expansion@aux-elem{ (Parsing)}}
|
||||
|
||||
Expansion recursively processes a @tech{syntax object}. Binding
|
||||
information on a syntax object drives the expansion process, and the
|
||||
expansion process generates new syntax objects that are like old ones,
|
||||
but enriched with new binding information.
|
||||
|
||||
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
@subsection[#:tag "mz:fully-expanded"]{Fully Expanded Programs}
|
||||
|
||||
A complete expansion produces a @tech{syntax object} matching the
|
||||
following grammar:
|
||||
|
||||
@schemegrammar*[
|
||||
#:literals (#%expression module #%plain-module-begin begin provide
|
||||
define-values define-syntaxes define-values-for-syntax
|
||||
require require-for-syntax require-for-template
|
||||
#%plain-lambda case-lambda if begin begin0 let-values letrec-values
|
||||
set! quote-syntax quote with-continuation-mark
|
||||
#%plain-app #%datum #%top #%variable-reference)
|
||||
[top-level-form general-top-level-form
|
||||
(#%expression expr)
|
||||
(module id name-id
|
||||
(#%plain-module-begin
|
||||
module-level-form ...))
|
||||
(begin top-level-form ...)]
|
||||
[module-level-form general-top-level-form
|
||||
(provide provide-spec ...)]
|
||||
[general-top-level-form expr
|
||||
(define-values (id ...) expr)
|
||||
(define-syntaxes (id ...) expr)
|
||||
(define-values-for-syntax (id ...) expr)
|
||||
(require require-spec ...)
|
||||
(require-for-syntax require-spec ...)
|
||||
(require-for-template require-spec ...)]
|
||||
[expr id
|
||||
(#%plain-lambda formals expr ...+)
|
||||
(case-lambda (formals expr ...+) ...)
|
||||
(if expr expr)
|
||||
(if expr expr expr)
|
||||
(begin expr ...+)
|
||||
(begin0 expr expr ...)
|
||||
(let-values (((id ...) expr) ...)
|
||||
expr ...+)
|
||||
(letrec-values (((id ...) expr) ...)
|
||||
expr ...+)
|
||||
(set! id expr)
|
||||
(#, @scheme[quote] datum)
|
||||
(quote-syntax datum)
|
||||
(with-continuation-mark expr expr expr)
|
||||
(#%plain-app expr ...+)
|
||||
(#%top . id)
|
||||
(#%variable-reference id)
|
||||
(#%variable-reference (#%top . id))]
|
||||
[formals (id ...)
|
||||
(id ...+ . id)
|
||||
id]]
|
||||
|
||||
This fully-expanded @tech{syntax object} corresponds to a
|
||||
@deftech{parse} of the expression (i.e., a @deftech{parsed}
|
||||
expression), and lexical information on its @tech{identifiers}
|
||||
indicates the @tech{parse}.
|
||||
|
||||
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
@subsection{Expansion Steps}
|
||||
|
||||
A step in parsing a form represented as a syntax object depends on its
|
||||
outermost shape:
|
||||
|
||||
@itemize{
|
||||
|
||||
@item{If it is an @tech{identifier} (i.e., a syntax-object symbol), then a
|
||||
binding is determined using symbol along with the lexical
|
||||
information in the identifier. If the identifier has a binding
|
||||
other than as a top-level variable, it is used to continue. If
|
||||
the identifier has no binding, a new syntax-object symbol
|
||||
@scheme['#%top] is created using the lexical context of the
|
||||
identifier; if this @schemeidfont{#%top} identifier has no
|
||||
binding (other than as a top-level variable), then parsing
|
||||
fails with an @scheme[exn:fail:syntax] exception. Otherwise,
|
||||
the new identifier is combined with the original identifier in
|
||||
a new syntax-object pair (using the same context as the
|
||||
original identifier), and the @schemeidfont{#%top} binding is
|
||||
used to continue.}
|
||||
|
||||
@item{If it is a syntax-object pair whose first element is an
|
||||
identifier, and if the identifier has a binding other than as a
|
||||
top-level variable, then the identifier's binding is used to
|
||||
continue.}
|
||||
|
||||
@item{If it is a syntax-object pair of any other form, then a new
|
||||
syntax-object symbol @scheme['#%app] is created using the
|
||||
lexical context of the pair. If the resulting
|
||||
@schemeidfont{#%app} identifier has no binding, parsing fails
|
||||
with an @scheme[exn:fail:syntax] exception. Otherwise, the new
|
||||
identifier is combined with the original pair to form a new
|
||||
syntax-object pair (using the same context as the original
|
||||
pair), and the @schemeidfont{#%app} binding is used to
|
||||
continue.}
|
||||
|
||||
@item{If it is any other syntax object, then a new syntax-object
|
||||
symbol @scheme['#%datum] is created using the lexical context
|
||||
of the original syntax object. If the resulting
|
||||
@schemeidfont{#%datum} identifier has no binding, parsing fails
|
||||
with an @scheme[exn:fail:syntax] exception. Otherwise, the new
|
||||
identifier is combined with the original syntax object in a new
|
||||
syntax-object pair (using the same context as the original
|
||||
pair), and the @schemeidfont{#%datum} binding is used to
|
||||
continue.}
|
||||
|
||||
}
|
||||
|
||||
Thus, the possibilities that do not fail lead to an identifier with a
|
||||
particular binding. This binding refers to one of three things:
|
||||
|
||||
@itemize{
|
||||
|
||||
@item{A transformer binding, such as introduced by
|
||||
@scheme[define-syntax] or @scheme[let-syntax]. If the
|
||||
associated value is to a procedure of one argument, the
|
||||
procedure is called as a syntax transformer (see
|
||||
@secref["transformers"]), and parsing starts again with the
|
||||
syntax-object result. If the transformer binding is to any
|
||||
other kind of value, parsing fails with an
|
||||
@scheme[exn:fail:syntax] exception.}
|
||||
|
||||
@item{A variable binding, such as introduced by a module-level
|
||||
@scheme[define] or by @scheme[let]. In this case, if the form
|
||||
being parsed is just an identifier, then it is parsed as a
|
||||
run-time reference to the location of the corresponding
|
||||
variable. If the form being parsed is a syntax-object list,
|
||||
then an @scheme[#%app] is added to the front of the
|
||||
syntax-object list in the same way as when the first item in
|
||||
the syntax-object list is not an identifier (third case in the
|
||||
previous enumeration), and parsing continues.}
|
||||
|
||||
@item{Core syntax, which is parsed as described in
|
||||
@secref["mz:syntax"]. Parsing core syntactic forms typically
|
||||
involves recursive parsing of sub-forms, and may introduce
|
||||
bindings that control the parsing of sub-forms.}
|
||||
|
||||
}
|
||||
|
||||
Each expansion step occurs in a particular context, and transformers
|
||||
and core-syntax parsing can depend on the context. For example, a
|
||||
@scheme[module] form is allowed only in a top-level context. The
|
||||
possible contexts are as follows:
|
||||
|
||||
@itemize{
|
||||
|
||||
@item{@defterm{top level} : outside of any module, definition, or
|
||||
expression, except that sub-expressions of a top-level
|
||||
@scheme[begin] form are also expanded as top-level forms.}
|
||||
|
||||
@item{@defterm{module begin} : inside the body of a module, as the
|
||||
only form within the module.}
|
||||
|
||||
@item{@defterm{module body} : in the body of a module (inside the
|
||||
moudule-begin layer).}
|
||||
|
||||
@item{@defterm{internal definition} : in a nested context that allows
|
||||
both definitions and expressions.}
|
||||
|
||||
@item{@defterm{expression} : in a context where only expressions are
|
||||
allowed.}
|
||||
|
||||
}
|
||||
|
||||
Different core syntax forms parse sub-forms in different contexts. For
|
||||
example, a @scheme[let] form always parses the right-hand expressions
|
||||
of a binding in an expression context, but it starts parsing the body
|
||||
in an internal-definition context.
|
||||
|
||||
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
@subsection[#:tag "mz:intdef-body"]{Internal Definitions}
|
||||
|
||||
An internal-definition context corresponds to a partial expansion
|
||||
step. A form that supports internal definitions starts by expanding
|
||||
its first form in an internal-definition context, but only
|
||||
partially. That is, it recursively expands only until the form becomes
|
||||
one of the following:
|
||||
|
||||
@itemize{
|
||||
|
||||
@item{A @scheme[define-values] or @scheme[define-syntaxes] form, for
|
||||
any form other than the last one: The definition form is not
|
||||
expanded further. Instead, the next form is expanded partially,
|
||||
and so on. As soon as an expression form is found, the
|
||||
accumulated definition forms are converted to a
|
||||
@scheme[letrec-values] (if no @scheme[define-syntaxes] forms
|
||||
were found) or @scheme[letrec-syntaxes+values] form, moving the
|
||||
expression forms to the body to be expanded in expression
|
||||
context.
|
||||
|
||||
When a @scheme[define-values] form is discovered, the lexical
|
||||
context of all syntax objects for the body sequence is
|
||||
immediately enriched with bindings for the
|
||||
@scheme[define-values] form before expansion continues. When a
|
||||
@scheme[define-syntaxes] form is discovered, the right-hand
|
||||
side is executed and a transformer binding is installed for the
|
||||
body sequence before expansion continues.}
|
||||
|
||||
@item{A primitive expression form other than @scheme[begin]: The
|
||||
expression is expanded in an expression context, along with all
|
||||
remaining body forms. If any definitions were found, this
|
||||
expansion takes place after conversion to a
|
||||
@scheme[letrec-values] or @scheme[letrec-syntaxes+values]
|
||||
form. Otherwise, the expressions are expanded immediately.}
|
||||
|
||||
@item{A @scheme[begin] form: The sub-forms of the @scheme[begin] are
|
||||
spliced into the internal-definition sequence, and partial
|
||||
expansion continues with the first of the newly-spliced forms
|
||||
(or the next form, if the @scheme[begin] had no sub-forms).}
|
||||
|
||||
}
|
||||
|
||||
If the last expression form turns out to be a @scheme[define-values]
|
||||
or @scheme[define-syntaxes] form, expansion fails with a syntax error.
|
||||
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Compilation}
|
||||
|
||||
Before expanded code is evaluated, it is first @deftech{compiled}. A
|
||||
compiled form has essentially the same information as the
|
||||
corresponding expanded form, though the internal representation
|
||||
naturally dispenses with identifiers for syntactic forms and local
|
||||
bindings. One significant difference is that a compiled form is almost
|
||||
entirely opaque, so the information that it contains cannot be
|
||||
accessed directly (which is why some identifiers can be dropped). At
|
||||
the same time, a compiled form can be marshaled to and from a byte
|
||||
string, so it is suitable for saving and re-loading code.
|
||||
|
||||
Although individual read, expand, compile, and evaluate operations are
|
||||
available, the operations are often combined automatically. For
|
||||
example, the @scheme[eval] procedure takes a syntax object and expands
|
||||
it, compiles it, and evaluates it.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Namespaces}
|
||||
|
||||
A @deftech{namespace} is a top-level mapping from symbols to binding
|
||||
information. It is the starting point for expanding an expression; a
|
||||
@tech{syntax object} produced by @scheme[read-syntax] has no initial
|
||||
lexical context; the @tech{syntax object} can be expanded after
|
||||
initializing it with the mappings of a particular namespace. A
|
||||
namespace is also the starting point evaluating expanded code, where
|
||||
the first step in evaluation is linking the code to specific module
|
||||
instances and top-level variables.
|
||||
|
||||
For expansion purposes, a namespace maps each symbol to one of three
|
||||
possible bindings:
|
||||
|
||||
@itemize{
|
||||
|
||||
@item{a particular module-level binding from a particular module}
|
||||
|
||||
@item{a top-level transformer binding named by the symbol}
|
||||
|
||||
@item{a top-level variable named by the symbol}
|
||||
|
||||
}
|
||||
|
||||
An ``empty'' namespace maps all symbols to top-level variables.
|
||||
Certain evaluations extend a namespace for future expansions;
|
||||
importing a module into the top-level adjusts the namespace bindings
|
||||
for all of the imported named, and evaluating a top-level
|
||||
@scheme[define] form updates the namespace's mapping to refer to a
|
||||
variable (in addition to installing a value into the variable).
|
||||
|
||||
For evaluation, each namespace encapsulates a distinct set of
|
||||
top-level variables, as well as a potentially distinct set of module
|
||||
instances. After a namespace is created, module instances from
|
||||
existing namespaces can be attached to the new namespace. In terms of
|
||||
the evaluation model, top-level variables from different namespaces
|
||||
essentially correspond to definitions with different prefixes.
|
||||
Furthermore, the first step in evaluating any compiled expression is
|
||||
to link its top-level variable and module-level variable references to
|
||||
specific variables in the namespace.
|
||||
|
||||
At all times during evaluation, some namespace is designated as the
|
||||
@deftech{current namespace}. The current namespace has no particular
|
||||
relationship, however, with the namespace that was used to expand the
|
||||
code that is executing, or with the namespace that was used to link
|
||||
the compiled form of the currently evaluating code. In particular,
|
||||
changing the current namespace during evaluation does not change the
|
||||
variables to which executing expressions refer. The current namespace
|
||||
only determines the behavior of (essentially reflective) operations to
|
||||
expand code and to start evaluating expanded/compiled code.
|
||||
|
||||
A namespace is purely a top-level entity, not to be confused with an
|
||||
environment. In particular, a namespace does not encapsulate the full
|
||||
environment of an expression inside local-binding forms.
|
|
@ -527,7 +527,7 @@ in tail position only if no @scheme[body]s are present.
|
|||
]}
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Continuation Marks: @scheme[with-continuation-marks]}
|
||||
@section{Continuation Marks: @scheme[with-continuation-mark]}
|
||||
|
||||
@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
|
||||
|
|
Loading…
Reference in New Issue
Block a user