eval section of guide

svn: r9922
This commit is contained in:
Matthew Flatt 2008-05-21 15:41:57 +00:00
parent 40fc919765
commit c5ef53525a

View File

@ -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}.}
}
}