1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
|
@node Macros in concert with modules
@section Macros in concert with modules
One reason that the standard Scheme language does not support a module
system yet is the issue of macros and modularity. There are several
issues to deal with:
@itemize @bullet
@cindex separate compilation
@item
that compilation of code that uses macros requires presence of those
macros' definitions, which prevents true separate compilation, because
those macros may be from other modules;
@cindex hygiene of macros in modules
@cindex referential transparency of macros in modules
@cindex macro hygiene in modules
@cindex macro referential transparency in modules
@item
that a macro's expansion must preserve referential transparency and
hygiene, for example in cases where it refers to names from within the
module in which it was defined, even if those names weren't exported;
and
@cindex phase separation
@cindex towers of evaluation phases
@item
that a macro's code may be arbitrary Scheme code, which in turn can use
other modules, so one module's compile-time, when macros are expanded,
is another's run-time, when the code used in macros is executed by the
expander: this makes a tower of phases of code evaluation over which
some coherent control must be provided.
@end itemize
@noindent
Scheme48's module system tries to address all of these issues
coherently and comprehensively. Although it cannot offer @emph{total}
separate compilation, it can offer incremental compilation, and
compiled modules can be dumped to the file system & restored in the
process of incremental compilation.@footnote{While such facilities are
not built-in to Scheme48, there is a package to do this, which will
probably be integrated at some point soon into Scheme48.}
Scheme48's module system is also very careful to preserve non-local
module references from a macro's expansion. Macros in Scheme48 are
required to perform hygienic renaming in order for this preservation,
however; @pxref{Explicit renaming macros}. For a brief example,
consider the @code{delay} syntax for lazy evaluation. It expands to a
simple procedure call:
@lisp
(delay @var{expression})
@expansion{} (make-promise (lambda () @var{expression}))@end lisp
@noindent
However, @code{make-promise} is not exported from the @code{scheme}
structure. The expansion works correctly due to the hygienic renaming
performed by the @code{delay} macro transformer: when it hygienically
renames @code{make-promise}, the output contains not the symbol but a
special token that refers exactly to the binding of @code{make-promise}
from the environment in which the @code{delay} macro transformer was
defined. Special care is taken to preserve this information. Had
@code{delay} expanded to a simple S-expression with simple symbols, it
would have generated a free reference to @code{make-promise}, which
would cause run-time undefined variable errors, or, if the module in
which @code{delay} was used had its @emph{own} binding of or imported a
binding of the name @code{make-promise}, @code{delay}'s expansion
would refer to the wrong binding, and there could potentially be
drastic and entirely unintended impact upon its semantics.
@cindex reflective tower
@cindex syntactic tower
@cindex @code{for-syntax}
Finally, Scheme48's module system has a special design for the tower of
phases, called a @dfn{reflective tower}.@footnote{This would be more
accurately named `syntactic tower,' as it has nothing to do with
reflection.} Every storey represents the environment available at
successive macro levels. That is, when the right-hand side of a macro
definition or binding is evaluated in an environment, the next storey
in that environment's reflective tower is used to evaluate that macro
binding. For example, in this code, there are two storeys used in the
tower:
@lisp
(define (foo ...bar...)
(let-syntax ((baz ...quux...))
...zot...))@end lisp
@noindent
In order to evaluate code in one storey of the reflective tower, it is
necessary to expand all macros first. Most of the code in this example
will eventually be evaluated in the first storey of the reflective
tower (assuming it is an ordinary top-level definition), but, in order
to expand macros in that code, the @code{let-syntax} must be expanded.
This causes @code{...quux...} to be evaluated in the @emph{second}
storey of the tower, after which macro expansion can proceed, and long
after which the enclosing program can be evaluated.
@cindex @code{for-syntax}
The module system provides a simple way to manipulate the reflective
tower. There is a package clause, @code{for-syntax}, that simply
contains package clauses for the next storey in the tower. For
example, a package with the following clauses:
@lisp
(open scheme foo bar)
(for-syntax (open scheme baz quux))@end lisp
@noindent
has all the bindings of @code{scheme}, @code{foo}, & @code{bar}, at the
ground storey; and the environment in which macros' definitions are
evaluated provides everything from @code{scheme}, @code{baz}, &
@code{quux}.
With no @code{for-syntax} clauses, the @code{scheme} structure is
implicitly opened; however, if there are @code{for-syntax} clauses,
@code{scheme} must be explicitly opened.@footnote{This is actually only
in the default config package of the default development environment.
The full mechanism is very general.} Also, @code{for-syntax} clauses
may be arbitrarily nested: reflective towers are theoretically infinite
in height. (They are internally implemented lazily, so they grow
exactly as high as they need to be.)
Here is a simple, though contrived, example of using @code{for-syntax}.
The @code{while-loops} structure exports @code{while}, a macro similar
to C's @code{while} loop. @code{While}'s transformer unhygienically
binds the name @code{exit} to a procedure that exits from the loop.
It necessarily, therefore, uses @embedref{Explicit renaming macros,
explicit renaming macros} in order to break hygiene; it also, in the
macro transformer, uses the @code{destructure} macro to destructure the
input form (@pxref{Library utilities}, in particular, the structure
@code{destructuring} for destructuring S-expressions).
@lisp
(define-structure while-loops (export while)
(open scheme)
(for-syntax (open scheme destructuring))
(begin
(define-syntax while
(lambda (form r compare)
(destructure (((WHILE test . body) form))
`(,(r 'CALL-WITH-CURRENT-CONTINUATION)
(,(r 'LAMBDA) (EXIT)
(,(r 'LET) (r 'LOOP) ()
(,(r 'IF) ,test
(,(r 'BEGIN)
,@@body
(,(r 'LOOP)))))))))
(CALL-WITH-CURRENT-CONTINUATION LAMBDA LET IF BEGIN))))@end lisp
This next @code{while-example} structure defines an example procedure
@code{foo} that uses @code{while}. Since @code{while-example} has no
macro definitions, there is no need for any @code{for-syntax} clauses;
it imports @code{while} from the @code{while-loops} structure only at
the ground storey, because it has no macro bindings to evaluate the
transformer expressions of:
@lisp
(define-structure while-example (export foo)
(open scheme while-loops)
(begin
(define (foo x)
(while (> x 9)
(if (integer? (sqrt x))
(exit (expt x 2))
(set! x (- x 1)))))))@end lisp
|