eval section of guide
svn: r9922
This commit is contained in:
parent
40fc919765
commit
c5ef53525a
|
@ -4,18 +4,220 @@
|
||||||
scheme/class
|
scheme/class
|
||||||
"guide-utils.ss")
|
"guide-utils.ss")
|
||||||
|
|
||||||
@title[#:tag "reflection"]{Reflection and Dynamic Evaluation}
|
@title[#:tag "reflection" #:style 'toc]{Reflection and Dynamic Evaluation}
|
||||||
|
|
||||||
Scheme is a @italic{dynamic} language. It offers numerous facilities
|
Scheme is a @italic{dynamic} language. It offers numerous facilities
|
||||||
for loading, compiling, and even constructing new code at run
|
for loading, compiling, and even constructing new code at run
|
||||||
time.
|
time.
|
||||||
|
|
||||||
|
@local-table-of-contents[]
|
||||||
|
|
||||||
@; ----------------------------------------------------------------------
|
@; ----------------------------------------------------------------------
|
||||||
|
|
||||||
@section{Namespaces}
|
@section[#:tag "eval"]{@scheme[eval]}
|
||||||
|
|
||||||
Dynamic evaluation requires a @deftech{namespace}, which encapsulates
|
The @scheme[eval] function takes a ``quoted'' expression or definition
|
||||||
two pieces of information:
|
and evaluates it:
|
||||||
|
|
||||||
|
@interaction[
|
||||||
|
(eval '(+ 1 2))
|
||||||
|
]
|
||||||
|
|
||||||
|
The power of @scheme[eval] that is that an expression can be
|
||||||
|
constructed dynamically:
|
||||||
|
|
||||||
|
@interaction[
|
||||||
|
(define (eval-formula formula)
|
||||||
|
(eval `(let ([x 2]
|
||||||
|
[y 3])
|
||||||
|
,formula)))
|
||||||
|
(eval-formula '(+ x y))
|
||||||
|
(eval-formula '(+ (* x y) y))
|
||||||
|
]
|
||||||
|
|
||||||
|
Of course, if we just wanted to evaluate expressions with given values
|
||||||
|
for @scheme[x] and @scheme[y], we do not need @scheme[eval]. A more
|
||||||
|
direct approach is to use first-class functions:
|
||||||
|
|
||||||
|
@interaction[
|
||||||
|
(define (apply-formula formula-proc)
|
||||||
|
(formula-proc 2 3))
|
||||||
|
(apply-formula (lambda (x y) (+ x y)))
|
||||||
|
(apply-formula (lambda (x y) (+ (* x y) y)))
|
||||||
|
]
|
||||||
|
|
||||||
|
However, if expressions like @scheme[(+ x y)] and @scheme[(+ (* x y)
|
||||||
|
y)] are read from a file supplied by a user, for example, then
|
||||||
|
@scheme[eval] might be appropriate. Simialrly, the @tech{REPL} reads
|
||||||
|
expressions that are typed by a user and uses @scheme[eval] to
|
||||||
|
evaluate them.
|
||||||
|
|
||||||
|
Also, @scheme[eval] is often used directly or indirectly on whole
|
||||||
|
modules. For example, a program might load a module on demand using
|
||||||
|
@scheme[dynamic-require], which is essentially a wrapper around
|
||||||
|
@scheme[eval] to dynamically load the module code.
|
||||||
|
|
||||||
|
@; ----------------------------------------
|
||||||
|
|
||||||
|
@subsection{Local Scopes}
|
||||||
|
|
||||||
|
The @scheme[eval] function cannot see local bindings in the context
|
||||||
|
where it is called. For example, calling @scheme[eval] inside an
|
||||||
|
unquoted @scheme[let] form to evaluate a formula does not make values
|
||||||
|
visible for @scheme[x] and @scheme[y]:
|
||||||
|
|
||||||
|
@interaction[
|
||||||
|
(define (broken-eval-formula formula)
|
||||||
|
(let ([x 2]
|
||||||
|
[y 3])
|
||||||
|
(eval formula)))
|
||||||
|
(broken-eval-formula '(+ x y))
|
||||||
|
]
|
||||||
|
|
||||||
|
The @scheme[eval] function cannot see the @scheme[x] and @scheme[y]
|
||||||
|
bindings precisely because it is a function, and Scheme is a lexically
|
||||||
|
scoped language. Imagine if @scheme[eval] were implemented as
|
||||||
|
|
||||||
|
@schemeblock[
|
||||||
|
(define (eval x)
|
||||||
|
(eval-expanded (macro-expand x)))
|
||||||
|
]
|
||||||
|
|
||||||
|
then at the point when @scheme[eval-expanded] is called, the most
|
||||||
|
recent binding of @scheme[x] is to the expression to evaluate, not the
|
||||||
|
@scheme[let] binding in @scheme[broken-eval-formula]. Lexical scope
|
||||||
|
prevents such confusing and fragile behavior, and consequently
|
||||||
|
prevents @scheme[eval] from seeing local bindings in the context where
|
||||||
|
it is called.
|
||||||
|
|
||||||
|
You might imagine that even though @scheme[eval] cannot see the local
|
||||||
|
bindings in @scheme[broken-eval-formula], there must actually be a
|
||||||
|
data structure mapping @scheme[x] to @scheme[2] and @scheme[y] to
|
||||||
|
@scheme[3], and you would like a way to get that data structure. In
|
||||||
|
fact, no such data structure exists; the compiler is free to replace
|
||||||
|
every use of @scheme[x] with @scheme[2] at compile time, so that the
|
||||||
|
local binding of @scheme[x] does not exist in any concrete sense at
|
||||||
|
run-time. Even when variables cannot be eliminated by
|
||||||
|
constant-folding, normally the names of the variables can be
|
||||||
|
eliminated, and the data structures that hold local values do not
|
||||||
|
resemble a mapping from names to values.
|
||||||
|
|
||||||
|
@; ----------------------------------------
|
||||||
|
|
||||||
|
@subsection[#:tag "namespaces"]{Namespaces}
|
||||||
|
|
||||||
|
Since @scheme[eval] cannot see the bindings from the context where it
|
||||||
|
is called, another mechanism is needed to determine dynamically
|
||||||
|
available bindings. A @deftech{namespace} is a first-class value that
|
||||||
|
encapsulates the bindings available for dynamic evaluation.
|
||||||
|
|
||||||
|
@margin-note{Informally, the term @defterm{namespace} is sometimes
|
||||||
|
used interchangeably with @defterm{environment} or
|
||||||
|
@defterm{scope}. In PLT Scheme, the term @defterm{namespace} has the
|
||||||
|
more specific, dynamic meaning given above, and it should not be
|
||||||
|
confused with static lexical concepts.}
|
||||||
|
|
||||||
|
Some functions, such as @scheme[eval], accept an optional namespace
|
||||||
|
argument. More often, the namespace used by a dynamic operation is the
|
||||||
|
@deftech{current namespace} as determined by the
|
||||||
|
@scheme[current-namespace] parameter.
|
||||||
|
|
||||||
|
When @scheme[eval] is used in a @tech{REPL}, the current is the one
|
||||||
|
that the @tech{REPL} uses for evaluating expressions. That's why the
|
||||||
|
following interaction successfully accesses @scheme[x] via
|
||||||
|
@scheme[eval]:
|
||||||
|
|
||||||
|
@interaction[
|
||||||
|
(define x 3)
|
||||||
|
(eval 'x)
|
||||||
|
]
|
||||||
|
|
||||||
|
In contrast, try the following a simple module and running in directly
|
||||||
|
in DrScheme's @onscreen{Module} language or supplying the file as a
|
||||||
|
command-line argument to @exec{mzscheme}:
|
||||||
|
|
||||||
|
@schememod[
|
||||||
|
scheme
|
||||||
|
|
||||||
|
(eval '(cons 1 2))
|
||||||
|
]
|
||||||
|
|
||||||
|
This fails because the initial current namespace is empty. When you
|
||||||
|
run @exec{mzscheme} in interactive mode (see
|
||||||
|
@secref["start-interactive-mode"]), the initial namespace is
|
||||||
|
initialized with the exports of the @scheme[scheme] module, but when
|
||||||
|
you run a module directly, the initial namespace starts empty.
|
||||||
|
|
||||||
|
In general, it's a bad idea to use @scheme[eval] with whatever
|
||||||
|
namespace happens to be installed. Instead, create a namespace
|
||||||
|
explicitly and install it for the call to eval:
|
||||||
|
|
||||||
|
@schememod[
|
||||||
|
scheme
|
||||||
|
|
||||||
|
(define ns (make-base-namespace))
|
||||||
|
(eval '(cons 1 2) ns) (code:comment #, @t{works})
|
||||||
|
]
|
||||||
|
|
||||||
|
The @scheme[make-base-namespace] function creates a namespace that is
|
||||||
|
initialized with the exports of @scheme[scheme/base]. The later
|
||||||
|
section @secref["mk-namespace"] provides more information on creating
|
||||||
|
and configuring namespaces.
|
||||||
|
|
||||||
|
@; ----------------------------------------
|
||||||
|
|
||||||
|
@subsection{Namespaces and Modules}
|
||||||
|
|
||||||
|
As with @scheme[let] bindings, lexical scope means that @scheme[eval]
|
||||||
|
cannot automatically see the definitions of a @scheme[module] in which
|
||||||
|
it is called. Unlike @scheme[let] bindings, however, Scheme provides a
|
||||||
|
way to reflect a module into a @tech{namespace}.
|
||||||
|
|
||||||
|
The @scheme[module->namespace] function takes a quoted @tech{module
|
||||||
|
path} and produces a namespace for evaluating expressions and
|
||||||
|
definitions as if they appears in the @scheme[module] body:
|
||||||
|
|
||||||
|
@interaction[
|
||||||
|
(module m scheme/base
|
||||||
|
(define x 11))
|
||||||
|
(require 'm)
|
||||||
|
(define ns (module->namespace ''m))
|
||||||
|
(eval 'x ns)
|
||||||
|
]
|
||||||
|
|
||||||
|
@margin-note{The double quoting in @scheme[''m] is because @scheme['m]
|
||||||
|
is a module path that refers to an interactively declared module, and
|
||||||
|
so @scheme[''m] is the quoted form of the path.}
|
||||||
|
|
||||||
|
The @scheme[module->namespace] function is mostly useful from outside
|
||||||
|
a module, where the module's full name is known. Inside a
|
||||||
|
@scheme[module] form, however, the full name of a module may not be
|
||||||
|
known, because it may depend on where the module source is location
|
||||||
|
when it is eventually loaded.
|
||||||
|
|
||||||
|
From within a @scheme[module], use @scheme[define-namespace-anchor] to
|
||||||
|
declare a reflection hook on the module, and use
|
||||||
|
@scheme[namespace-anchor->namespace] to reel in the module's
|
||||||
|
namespace:
|
||||||
|
|
||||||
|
@schememod[
|
||||||
|
scheme
|
||||||
|
|
||||||
|
(define-namespace-anchor a)
|
||||||
|
(define ns (namespace-anchor->namespace a))
|
||||||
|
|
||||||
|
(define x 1)
|
||||||
|
(define y 2)
|
||||||
|
|
||||||
|
(eval '(cons x y) ns) (code:comment #, @t{produces @schemeresult[(1 . 2)]})
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@; ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
@section[#:tag "mk-namespace"]{Manipulating Namespaces}
|
||||||
|
|
||||||
|
A @tech{namespace} encapsulates two pieces of information:
|
||||||
|
|
||||||
@itemize{
|
@itemize{
|
||||||
|
|
||||||
|
@ -47,20 +249,9 @@ supplied in the @tech{REPL}. Top-level @scheme[require] and
|
||||||
declarations (typically loaded on demand for a @scheme[require] form)
|
declarations (typically loaded on demand for a @scheme[require] form)
|
||||||
adjust the module mapping.
|
adjust the module mapping.
|
||||||
|
|
||||||
Informally, the term @defterm{namespace} is sometimes used
|
@; ----------------------------------------
|
||||||
interchangeably with @defterm{environment} or @defterm{scope}. In PLT
|
|
||||||
Scheme, the term @defterm{namespace} has the more specific, dynamic
|
|
||||||
meaning given above, and it should not be confused with static lexical
|
|
||||||
concepts.
|
|
||||||
|
|
||||||
@; ----------------------------------------------------------------------
|
@subsection{Creating and Installing Namespaces}
|
||||||
|
|
||||||
@section{Creating and Installing Namespaces}
|
|
||||||
|
|
||||||
A @tech{namespace} is a first-class value. Some functions, such as
|
|
||||||
@scheme[eval], accept an optional namespace argument. More often, the
|
|
||||||
namespace used by a dynamic operation is the @deftech{current
|
|
||||||
namespace} as determined by the @scheme[current-namespace] parameter.
|
|
||||||
|
|
||||||
The function @scheme[make-empty-namespace] creates a new, empty
|
The function @scheme[make-empty-namespace] creates a new, empty
|
||||||
@tech{namespace}. Since the namespace is truly empty, it cannot at
|
@tech{namespace}. Since the namespace is truly empty, it cannot at
|
||||||
|
@ -126,9 +317,9 @@ for @schemeidfont{require} and make a subsequent @scheme[(eval
|
||||||
more compact, but also because it avoids introducing bindings that are
|
more compact, but also because it avoids introducing bindings that are
|
||||||
not part of the domain-specific languages.
|
not part of the domain-specific languages.
|
||||||
|
|
||||||
@; ----------------------------------------------------------------------
|
@; ----------------------------------------
|
||||||
|
|
||||||
@section{Sharing Data and Code Across Namespaces}
|
@subsection{Sharing Data and Code Across Namespaces}
|
||||||
|
|
||||||
Modules not attached to a new namespace will be loaded and
|
Modules not attached to a new namespace will be loaded and
|
||||||
instantiated afresh if they are demanded by evaluation. For example,
|
instantiated afresh if they are demanded by evaluation. For example,
|
||||||
|
@ -194,38 +385,3 @@ example, since the enclosing module requires
|
||||||
instance of @schememodname[scheme/class]. Moreover, that instance is
|
instance of @schememodname[scheme/class]. Moreover, that instance is
|
||||||
the same as the one imported into the module, so the class datatype is
|
the same as the one imported into the module, so the class datatype is
|
||||||
shared.
|
shared.
|
||||||
|
|
||||||
@;{
|
|
||||||
|
|
||||||
@; ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
@section{The Top Level is Hopeless}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@; ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
@section{Guidelines on @scheme[eval], @scheme[load], and @scheme[dynamic-require]}
|
|
||||||
|
|
||||||
These dynamic features are powerful tools, but they are also easily
|
|
||||||
misused. This section provides some general guidelines on using
|
|
||||||
dynamic features.
|
|
||||||
|
|
||||||
@itemize{
|
|
||||||
|
|
||||||
@item{If you find a use for @scheme[eval] or @scheme[load], then it's
|
|
||||||
probably an abuse. In any case, don't expect the environment
|
|
||||||
for dynamically evaluated code to have anything to do with the
|
|
||||||
environment of a call to @scheme[eval] or @scheme[load].}
|
|
||||||
|
|
||||||
@item{If you find a use for @scheme[dynamic-require], such as for a
|
|
||||||
plug-in architecture or to delay loading code, then it's likely
|
|
||||||
a fine use.}
|
|
||||||
|
|
||||||
@item{When using functions like @scheme[eval], @scheme[load], or
|
|
||||||
@scheme[dynamic-require], take care to install an appropriate
|
|
||||||
@tech{namespace}.}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user