As syntax-case
macros have the whole power of Scheme available to them,
they present a problem regarding time: when a macro runs, what parts of the
program are available for the macro to use?
The default answer to this question is that when you import a module (via
define-module
or use-modules
), that module will be loaded up at
expansion-time, as well as at run-time. Additionally, top-level syntactic
definitions within one compilation unit made by define-syntax
are also
evaluated at expansion time, in the order that they appear in the compilation
unit (file).
But if a syntactic definition needs to call out to a normal procedure at expansion-time, it might well need special declarations to indicate that the procedure should be made available at expansion-time.
For example, the following code tries to embed a compilation timestamp in the compiled bytecode using a macro that expands to the date as a string literal. It will work at a REPL, but not in a file, as it cannot be byte-compiled:
(use-modules (srfi srfi-19)) (define start-date (date->string (current-date))) (define-syntax *compilation-date* (lambda (syntax) start-date)) (display *compilation-date*) (newline)
It works at a REPL because the expressions are evaluated one-by-one, in order, but if placed in a file, the expressions are expanded one-by-one, but not evaluated until the compiled file is loaded.
The fix is to use eval-when
.
(use-modules (srfi srfi-19)) (eval-when (expand load eval) (define start-date (date->string (current-date)))) (define-syntax *compilation-date* (lambda (syntax) start-date)) (display *compilation-date*) (newline)
Evaluate exp... under the given conditions. Valid conditions include:
expand
Evaluate during macro expansion, whether compiling or not.
load
Evaluate during the evaluation phase of compiled code, e.g. when loading a compiled module or running compiled code at the REPL.
eval
Evaluate during the evaluation phase of non-compiled code.
compile
Evaluate during macro expansion, but only when compiling.
In other words, when using the primitive evaluator, eval-when
expressions with expand
are run during macro expansion, and those
with eval
are run during the evaluation phase.
When using the compiler, eval-when
expressions with either
expand
or compile
are run during macro expansion, and
those with load
are run during the evaluation phase.
When in doubt, use the three conditions (expand load eval)
, as in
the example above. Other uses of eval-when
may void your
warranty or poison your cat.