The eval function takes a “quoted” expression or definition and evaluates it:
|> (eval '(+ 1 2))|
The power of eval that is that an expression can be constructed dynamically:
|> (eval-formula '(+ x y))|
|> (eval-formula '(+ (* x y) y))|
Of course, if we just wanted to evaluate expressions with given values for x and y, we do not need eval. A more direct approach is to use first-class functions:
|> (apply-formula (lambda (x y) (+ x y)))|
|> (apply-formula (lambda (x y) (+ (* x y) y)))|
However, if expressions like (+ x y) and (+ (* x y) y) are read from a file supplied by a user, for example, then eval might be appropriate. Simialrly, the REPL reads expressions that are typed by a user and uses eval to evaluate them.
Also, eval is often used directly or indirectly on whole modules. For example, a program might load a module on demand using dynamic-require, which is essentially a wrapper around eval to dynamically load the module code.
|> (broken-eval-formula '(+ x y))|
reference to undefined identifier: x
|(define (eval x)|
|(eval-expanded (macro-expand x)))|
then at the point when eval-expanded is called, the most recent binding of x is to the expression to evaluate, not the let binding in broken-eval-formula. Lexical scope prevents such confusing and fragile behavior, and consequently prevents eval from seeing local bindings in the context where it is called.
You might imagine that even though eval cannot see the local bindings in broken-eval-formula, there must actually be a data structure mapping x to 2 and y to 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 x with 2 at compile time, so that the local binding of 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.
Since eval cannot see the bindings from the context where it is called, another mechanism is needed to determine dynamically available bindings. A namespace is a first-class value that encapsulates the bindings available for dynamic evaluation.
Informally, the term namespace is sometimes used interchangeably with environment or scope. In PLT Scheme, the term namespace has the more specific, dynamic meaning given above, and it should not be confused with static lexical concepts.
|> (define x 3)|
|> (eval 'x)|
In contrast, try the following a simple module and running in directly in DrScheme’s Module language or supplying the file as a command-line argument to mzscheme:
|(eval '(cons 1 2))|
This fails because the initial current namespace is empty. When you run mzscheme in interactive mode (see Interactive Mode), the initial namespace is initialized with the exports of the scheme module, but when you run a module directly, the initial namespace starts empty.
In general, it’s a bad idea to use eval with whatever namespace happens to be installed. Instead, create a namespace explicitly and install it for the call to eval:
|(define ns (make-base-namespace))|
|(eval '(cons 1 2) ns) ; works|
The make-base-namespace function creates a namespace that is initialized with the exports of scheme/base. The later section Manipulating Namespaces provides more information on creating and configuring namespaces.
As with let bindings, lexical scope means that eval cannot automatically see the definitions of a module in which it is called. Unlike let bindings, however, Scheme provides a way to reflect a module into a namespace.
|> (require 'm)|
|> (define ns (module->namespace ''m))|
|> (eval 'x ns)|
The double quoting in ''m is because 'm is a module path that refers to an interactively declared module, and so ''m is the quoted form of the path.
The module->namespace function is mostly useful from outside a module, where the module’s full name is known. Inside a 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.
|(define ns (namespace-anchor->namespace a))|
|(define x 1)|
|(define y 2)|
|(eval '(cons x y) ns) ; produces (1 . 2)|