add docs for lazy-require-syntax
This commit is contained in:
parent
8d04bd67d6
commit
ea0efd9e14
|
@ -3049,7 +3049,7 @@ If the enclosing relative phase level is not 0, then
|
|||
@racket[module-path] is also placed in a submodule (with a use of
|
||||
@racket[define-runtime-module-path-index] at phase level 0 within the
|
||||
submodule). Introduced submodules have the names
|
||||
@racket[lazy-require-]@racket[_n]@racketidfont{-}@racket[_m], where
|
||||
@racket[lazy-require-aux]@racket[_n]@racketidfont{-}@racket[_m], where
|
||||
@racket[_n] is a phase-level number and @racket[_m] is a number.
|
||||
|
||||
When the use of a lazily-required function triggers module loading, it
|
||||
|
@ -3071,4 +3071,90 @@ process of compiling a module).
|
|||
]
|
||||
}
|
||||
|
||||
@defform[(lazy-require-syntax [module-path (macro-import ...)] ...)
|
||||
#:grammar
|
||||
([macro-import macro-id
|
||||
(orig-macro-id macro-id)])]{
|
||||
|
||||
Like @racket[lazy-require] but for macros. That is, it defines each
|
||||
@racket[macro-id] as a macro that, when used, dynamically loads the
|
||||
macro's implementation from the given @racket[module-path]. If
|
||||
@racket[orig-macro-id] is not given, it defaults to @racket[macro-id].
|
||||
|
||||
Use @racket[lazy-require-syntax] in the @emph{implementation} of a library
|
||||
with large, complicated macros to avoid a dependence from clients of
|
||||
the library on the macro ``compilers.'' Note that only macros with
|
||||
exceptionally large compile-time components (such as Typed Racket,
|
||||
which includes a type checker and optimizer) benefit from
|
||||
@racket[lazy-require-syntax]; typical macros do not.
|
||||
|
||||
@bold{Warning:} @racket[lazy-require-syntax] breaks the invariants
|
||||
that Racket's module loader and linker rely on; these invariants
|
||||
normally ensure that the references in code produced by a macro are
|
||||
loaded before the code runs. Safe use of @racket[lazy-require-syntax]
|
||||
requires a particular structure in the macro implementation. (In
|
||||
particular, @racket[lazy-require-syntax] cannot simply be introduced
|
||||
in the client code.) The macro implementation must follow these rules:
|
||||
@itemlist[#:style 'ordered
|
||||
@item{the interface module must @racket[require] the runtime-support module}
|
||||
@item{the compiler module must @racket[require] the runtime-support module via
|
||||
an @emph{absolute} module path rather than a @emph{relative} path}
|
||||
]
|
||||
|
||||
To explain the concepts of ``interface, compiler, and runtime-support
|
||||
modules'', here is an example module that exports a macro:
|
||||
@racketblock[ ;; @examples[#:eval lazy-require-eval #:label #f
|
||||
(module original racket/base
|
||||
(define (ntimes-proc n thunk)
|
||||
(for ([i (in-range n)]) (thunk)))
|
||||
(define-syntax-rule (ntimes n expr)
|
||||
(ntimes-proc n (lambda () expr)))
|
||||
(provide ntimes))
|
||||
]
|
||||
Suppose we want to use @racket[lazy-require-syntax] to lazily load the
|
||||
implementation of the @racket[ntimes] macro transformer. The original
|
||||
module must be split into three parts:
|
||||
@racketblock[ ;; @examples[#:eval lazy-require-eval #:label #f
|
||||
(module runtime-support racket/base
|
||||
(define (ntimes-proc n thunk)
|
||||
(for ([i (in-range n)]) (thunk)))
|
||||
(provide ntimes-proc))
|
||||
(module compiler racket/base
|
||||
(require 'runtime-support)
|
||||
(define-syntax-rule (ntimes n expr)
|
||||
(ntimes-proc n (lambda () expr)))
|
||||
(provide ntimes))
|
||||
(module interface racket/base
|
||||
(require racket/lazy-require)
|
||||
(require 'runtime-support)
|
||||
(lazy-require-syntax ['compiler (ntimes)])
|
||||
(provide ntimes))
|
||||
]
|
||||
The runtime support module contains the function and value definitions
|
||||
that the macro refers to. The compiler module contains the macro
|
||||
definition(s) themselves---the part of the code that ``disappears''
|
||||
after compile time. The interface module lazily loads the macro
|
||||
transformer, but it makes sure the runtime support module is defined at
|
||||
run time by requiring it normally. In a larger example, of course, the
|
||||
runtime support and compiler may both consist of multiple modules.
|
||||
|
||||
Here what happens when we don't separate the runtime support into a
|
||||
separate module:
|
||||
@examples[#:eval lazy-require-eval #:label #f
|
||||
(module bad-no-runtime racket/base
|
||||
(define (ntimes-proc n thunk)
|
||||
(for ([i (in-range n)]) (thunk)))
|
||||
(define-syntax-rule (ntimes n expr)
|
||||
(ntimes-proc n (lambda () expr)))
|
||||
(provide ntimes))
|
||||
(module bad-client racket/base
|
||||
(require racket/lazy-require)
|
||||
(lazy-require-syntax ['bad-no-runtime (ntimes)])
|
||||
(ntimes 3 (printf "hello?\n")))
|
||||
(eval:error (require 'bad-client))
|
||||
]
|
||||
A similar error occurs when the interface module doesn't introduce a
|
||||
dependency on the runtime support module.
|
||||
}
|
||||
|
||||
@(close-eval lazy-require-eval)
|
||||
|
|
Loading…
Reference in New Issue
Block a user