From c5ef53525afece5ea3a416bedad1f54b9812698d Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Wed, 21 May 2008 15:41:57 +0000 Subject: [PATCH] eval section of guide svn: r9922 --- collects/scribblings/guide/namespaces.scrbl | 264 ++++++++++++++++---- 1 file changed, 210 insertions(+), 54 deletions(-) diff --git a/collects/scribblings/guide/namespaces.scrbl b/collects/scribblings/guide/namespaces.scrbl index 692fa8f1f2..614bb99fa5 100644 --- a/collects/scribblings/guide/namespaces.scrbl +++ b/collects/scribblings/guide/namespaces.scrbl @@ -4,18 +4,220 @@ scheme/class "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 for loading, compiling, and even constructing new code at run time. +@local-table-of-contents[] + @; ---------------------------------------------------------------------- -@section{Namespaces} +@section[#:tag "eval"]{@scheme[eval]} -Dynamic evaluation requires a @deftech{namespace}, which encapsulates -two pieces of information: +The @scheme[eval] function takes a ``quoted'' expression or definition +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{ @@ -47,20 +249,9 @@ supplied in the @tech{REPL}. Top-level @scheme[require] and declarations (typically loaded on demand for a @scheme[require] form) 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. +@; ---------------------------------------- -@; ---------------------------------------------------------------------- - -@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. +@subsection{Creating and Installing Namespaces} The function @scheme[make-empty-namespace] creates a new, empty @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 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 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 the same as the one imported into the module, so the class datatype is 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}.} - -} - -}