On this page:
lazy-require
lazy-require-syntax

3.26 Importing Modules Lazily: lazy-require

The bindings documented in this section are provided by the racket/lazy-require library, not racket/base or racket.

syntax

(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
    [racket/list (partition)])
> (partition even? '(1 2 3 4 5))

'(2 4)

'(1 3 5)

> (module hello racket/base
    (provide hello)
    (printf "starting hello server\n")
    (define (hello) (printf "hello!\n")))
> (lazy-require
    ['hello ([hello greet])])
> (greet)

starting hello server

hello!

syntax

(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:
  1. the interface module must require the runtime-support module

  2. 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:
(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 lazy-require-syntax to lazily load the implementation of the ntimes macro transformer. The original module must be split into three parts:
(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:
> (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")))
> (require 'bad-client)

no module instance found:

#<resolved-module-path:'bad-no-runtime> 0

A similar error occurs when the interface module doesn’t introduce a dependency on the runtime support module.