3.26 Importing Modules Lazily: lazy-require
(lazy-require [module-path (fun-import ...)] ...)
|
|
fun-import | | = | | fun-id | | | | | | (orig-fun-id fun-id) |
|
Defines each fun-id as a function that, when called,
dynamically requires the export named orig-fun-id from the
module specified by module-path and calls it with the same
arguments. If orig-fun-id is not given, it defaults to
fun-id.
If the enclosing relative phase level is not 0, then
module-path is also placed in a submodule (with a use of
define-runtime-module-path-index at phase level 0 within the
submodule). Introduced submodules have the names
lazy-require-auxn-m, where
n is a phase-level number and m is a number.
When the use of a lazily-required function triggers module loading, it
also triggers a use of register-external-module to declare an
indirect compilation dependency (in case the function is used in the
process of compiling a module).
Examples:
(lazy-require-syntax [module-path (macro-import ...)] ...)
|
|
macro-import | | = | | macro-id | | | | | | (orig-macro-id macro-id) |
|
Like
lazy-require but for macros. That is, it defines each
macro-id as a macro that, when used, dynamically loads the
macro’s implementation from the given
module-path. If
orig-macro-id is not given, it defaults to
macro-id.
Use lazy-require-syntax in the 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
lazy-require-syntax; typical macros do not.
Warning: 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
lazy-require-syntax
requires a particular structure in the macro implementation. (In
particular,
lazy-require-syntax cannot simply be introduced
in the client code.) The macro implementation must follow these rules:
the interface module must require the runtime-support module
the compiler module must require the runtime-support module via
an absolute module path rather than a relative path
To explain the concepts of “interface, compiler, and runtime-support
modules”, here is an example module that exports a macro:
Suppose we want to use
lazy-require-syntax to lazily load the
implementation of the
ntimes macro transformer. The original
module must be split into three parts:
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:
|
|
> (require 'bad-client) |
require: namespace mismatch; |
reference to a module that is not instantiated |
module: 'bad-no-runtime |
phase: 0 |
A similar error occurs when the interface module doesn’t introduce a
dependency on the runtime support module.