6.8.8 Eval-when

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)
Syntax: eval-when conditions exp...

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.