3.26 Importing Modules Lazily: lazy-require

► |  | The Racket Reference

1 | Language Model
2 | Notation for Documentation
3 | Syntactic Forms
4 | Datatypes
5 | Structures
6 | Classes and Objects
7 | Units
8 | Contracts
9 | Pattern Matching
10 | Control Flow
11 | Concurrency and Parallelism
12 | Macros
13 | Input and Output
14 | Reflection and Security
15 | Operating System
16 | Memory Management
17 | Unsafe Operations
18 | Running Racket
| Bibliography
| Index

▼ | 3 | Syntactic Forms

3.1 | Modules: module, module*, ...
3.2 | Importing and Exporting: require and provide
3.3 | Literals: quote and #%datum
3.4 | Expression Wrapper: #%expression
3.5 | Variable References and #%top
3.6 | Locations: #%variable-reference
3.7 | Procedure Applications and #%app
3.8 | Procedure Expressions: lambda and case-lambda
3.9 | Local Binding: let, let*, letrec, ...
3.10 | Local Definitions: local
3.11 | Constructing Graphs: shared
3.12 | Conditionals: if, cond, and, and or
3.13 | Dispatch: case
3.14 | Definitions: define, define-syntax, ...
3.15 | Sequencing: begin, begin0, and begin-for-syntax
3.16 | Guarded Evaluation: when and unless
3.17 | Assignment: set! and set!-values
3.18 | Iterations and Comprehensions: for, for/ list, ...
3.19 | Continuation Marks: with-continuation-mark
3.20 | Quasiquoting: quasiquote, unquote, and unquote-splicing
3.21 | Syntax Quoting: quote-syntax
3.22 | Interaction Wrapper: #%top-interaction
3.23 | Blocks: block
3.24 | Internal-Definition Limiting: #%stratified-body
3.25 | Performance Hints: begin-encourage-inline
3.26 | Importing Modules Lazily: lazy-require

On this page:

lazy-require
lazy-require-syntax

top contents ← prev up next →

#### 3.26`` Importing Modules Lazily: lazy-require🔗ℹ

(require racket/lazy-require) | package: base

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)
> 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.
top contents ← prev up next →