14.4 Module Names and Loading
14.4.1 Resolving Module Names
The syntax/modresolve library provides additional operations for resolving and manipulating module names.
The name of a declared module is represented by a resolved module path, which encapsulates either a symbol or a complete filesystem path (see Paths). A symbol normally refers to a predefined module or module declared through reflective evaluation (e.g., eval). A filesystem path normally refers to a module declaration that was loaded on demand via require or other forms.
A module path is a datum that matches the grammar for module-path for require. A module path is relative to another module.
procedure
v : any/c
procedure
path :
(or/c symbol? (and/c path? complete-path?) (cons/c (or/c symbol? (and/c path? complete-path?)) (non-empty-listof symbol?)))
A resolved module path is interned. That is, if two resolved module path values encapsulate paths that are equal?, then the resolved module path values are eq?.
procedure
(resolved-module-path-name module-path)
→
(or/c symbol? (and/c path? complete-path?) (cons/c (or/c symbol? (and/c path? complete-path?)) (non-empty-listof symbol?))) module-path : resolved-module-path?
procedure
(module-path? v) → boolean?
v : any/c
parameter
→
(case-> (resolved-module-path? (or/c #f namespace?) . -> . any) (module-path? (or/c #f resolved-module-path?) (or/c #f syntax?) boolean? . -> . resolved-module-path?)) (current-module-name-resolver proc) → void?
proc :
(case-> (resolved-module-path? (or/c #f namespace?) . -> . any) (module-path? (or/c #f resolved-module-path?) (or/c #f syntax?) boolean? . -> . resolved-module-path?))
The default module name resolver uses collection-file-path to convert lib and symbolic-shorthand module paths to filesystem paths. The collection-file-path function, in turn, uses the current-library-collection-links and current-library-collection-paths parameters.
When given two arguments, the first is a name for a module that is now declared in the current namespace, and the second is optionally a namespace from which the declaration was copied. The module name resolver’s result in this case is ignored.
The current module name resolver is called with two arguments by namespace-attach-module or namespace-attach-module-declaration to notify the resolver that a module declaration was attached to the current namespace (and should not be loaded in the future for the namespace’s module registry). Evaluation of a module declaration also calls the current module name resolver with two arguments, where the first is the declared module and the second is #f. No other Racket operation invokes the module name resolver with two arguments, but other tools (such as DrRacket) might call this resolver in this mode to avoid redundant module loads.
When given four arguments, the first is a module path, equivalent to a quoted module-path for require. The second is name for the source module, if any, to which the path is relative; if the second argument is #f, the module path is relative to (or (current-load-relative-directory) (current-directory)). The third argument is a syntax object that can be used for error reporting, if it is not #f. If the last argument is #t, then the module declaration should be loaded (if it is not already), otherwise the module path should be simply resolved to a name. The result is the resolved name.
For the second case, the standard module name resolver keeps a table per module registry containing loaded module name. If a resolved module path is not in the table, and #f is not provided as the fourth argument to the module name resolver, then the name is put into the table and the corresponding file is loaded with a variant of load/use-compiled that passes the expected module name to the compiled-load handler.
While loading a file, the default module name resolver sets the current-module-declare-name parameter to the resolved module name (while the compiled-load handler sets current-module-declare-source). Also, the default module name resolver records in a private continuation mark the module being loaded, and it checks whether such a mark already exists; if such a continuation mark does exist in the current continuation, then the exn:fail exception is raised with a message about a dependency cycle.
The default module name resolver cooperates with the default compiled-load handler: on a module-attach notification, bytecode-file information recorded by the compiled-load handler for the source namespace’s module registry is transferred to the target namespace’s module registry.
The default module name resolver also maintains a small, module registry-specific cache that maps lib and symbolic module paths to their resolutions. This cache is consulted before checking parameters such as current-library-collection-links and current-library-collection-paths, so results may “stick” even if those parameter values change. An entry is added to the cache only when the fourth argument to the module name resolver is true (indicating that a module should be loaded) and only when loading succeeds.
Module loading is suppressed (i.e., #f is supplied as a fourth argument to the module name resolver) when resolving module paths in syntax objects (see Syntax Objects). When a syntax object is manipulated, the current namespace might not match the original namespace for the syntax object, and the module should not necessarily be loaded in the current namespace.
For historical reasons, the default module name resolver currently accepts three arguments, in addition to two and four. Three arguments are treated the same as four arguments with the fourth argument as #t, except that an error is also logged. Support for three arguments will be removed in a future version.
Changed in version 6.0.1.12 of package base: Added error logging to the default module name resolver when called with three arguments.
parameter
→ (or/c resolved-module-path? #f) (current-module-declare-name name) → void? name : (or/c resolved-module-path? #f)
When declaring submodules, current-module-declare-name determines the name used for the submodule’s root module, while its submodule path relative to the root module is unaffected.
parameter
→ (or/c symbol? (and/c path? complete-path?) #f) (current-module-declare-source src) → void? src : (or/c symbol? (and/c path? complete-path?) #f)
parameter
→
(or/c #f module-path? (and/c syntax? (lambda (stx) (module-path? (syntax->datum s))))) (current-module-path-for-load path) → void?
path :
(or/c #f module-path? (and/c syntax? (lambda (stx) (module-path? (syntax->datum s)))))
14.4.2 Compiled Modules and References
While expanding a module declaration, the expander resolves module paths for imports to load module declarations as necessary and to determine imported bindings, but the compiled form of a module declaration preserves the original module path. Consequently, a compiled module can be moved to another filesystem, where the module name resolver can resolve inter-module references among compiled code.
When a module reference is extracted from compiled form (see module-compiled-imports) or from syntax objects in macro expansion (see Syntax Object Content), the module reference is reported in the form of a module path index. A module path index is a semi-interned (multiple references to the same relative module tend to use the same module path index value, but not always) opaque value that encodes a module path (see module-path?) and either a resolved module path or another module path index to which it is relative.
A module path index that uses both #f for its path and
base module path index represents “self”—
A module path index has state. When it is resolved to a resolved module path, then the resolved module path is stored with the module path index. In particular, when a module is loaded, its root module path index is resolved to match the module’s declaration-time name. This resolved path is forgotten, however, in identifiers that the module contributes to the compiled and marshaled form of other modules. The transient nature of resolved names allows the module code to be loaded with a different resolved name than the name when it was compiled.
Two module path index values are equal? when they have equal? path and base values (even if they have different resolved values).
procedure
(module-path-index? v) → boolean?
v : any/c
procedure
mpi : module-path-index?
Resolving a module path index uses the current module name resolver (see current-module-name-resolver). Depending on the kind of module paths encapsulated by mpi, the computed resolved name can depend on the value of current-load-relative-directory or current-directory.
procedure
(module-path-index-split mpi)
→
(or/c module-path? #f) (or/c module-path-index? resolved-module-path? #f) mpi : module-path-index?
A #f second result means that the path is relative to an unspecified directory (i.e., its resolution depends on the value of current-load-relative-directory and/or current-directory).
A #f for the first result implies a #f for the second result, and means that mpi represents “self” (see above). Such a module path index may have a non-#f submodule path as reported by module-path-index-submodule.
procedure
→ (or/c #f (non-empty-listof symbol?)) mpi : module-path-index?
procedure
(module-path-index-join path base [submod]) → module-path-index?
path : (or/c module-path? #f) base : (or/c module-path-index? resolved-module-path? #f) submod : (or/c #f (non-empty-listof symbol?)) = #f
procedure
v : any/c
procedure
(module-compiled-name compiled-module-code)
→ (or/c symbol? (cons/c symbol? (non-empty-listof symbol?))) compiled-module-code : compiled-module-expression?
(module-compiled-name compiled-module-code name) → compiled-module-expression? compiled-module-code : compiled-module-expression? name : (or/c symbol? (cons/c symbol? (non-empty-listof symbol?)))
The name is a symbol for a top-level module, and it list of symbols for a submodule, where a list reflects the submodule path to the module starting with the top-level module’s declared name.
procedure
(module-compiled-submodules compiled-module-code non-star?) → (listof compiled-module-expression?) compiled-module-code : compiled-module-expression? non-star? : any/c
(module-compiled-submodules compiled-module-code non-star? submodules) → compiled-module-expression? compiled-module-code : compiled-module-expression? non-star? : any/c submodules : (listof compiled-module-expression?)
procedure
(module-compiled-imports compiled-module-code)
→
(listof (cons/c (or/c exact-integer? #f) (listof module-path-index?))) compiled-module-code : compiled-module-expression?
procedure
(module-compiled-exports compiled-module-code)
→
(listof (cons/c (or/c exact-integer? #f) list?)) (listof (cons/c (or/c exact-integer? #f) list?)) compiled-module-code : compiled-module-expression?
Returns two association lists mapping phase level values (where #f corresponds to the label phase level) to exports at the corresponding phase. The first association list is for exported variables, and the second is for exported syntax. Beware however, that value bindings re-exported though a rename transformer are in the syntax list instead of the value list.
Each associated list, which is represented by list? in the result contracts above, more precisely matches the contract
(listof (list/c symbol? (listof (or/c module-path-index? (list/c module-path-index? (or/c exact-integer? #f) symbol? (or/c exact-integer? #f))))))
For each element of the list, the leading symbol is the name of the export.
The second part—
For each origin, a module path index by itself means that the binding was imported with a phase level shift of 0 (i.e., a plain require without for-meta, for-syntax, etc.), and imported identifier has the same name as the re-exported name. An origin represented with a list indicates explicitly the import, the import phase level shift (where #f corresponds to a for-label import), the import name of the re-exported binding, and the phase level of the import.}
procedure
(module-compiled-language-info compiled-module-code)
→ (or/c #f (vector/c module-path? symbol? any/c)) compiled-module-code : compiled-module-expression?
See also Module-Handling Configuration in The Racket Guide.
Returns information intended to reflect the “language” of the module’s implementation as originally attached to the syntax of the module’s declaration though the 'module-language syntax property. See also module.
If no information is available for the module, the result is #f. Otherwise, the result is (vector mp name val) such that ((dynamic-require mp name) val) should return function that takes two arguments. The function’s arguments are a key for reflected information and a default value. Acceptable keys and the interpretation of results is up to external tools, such as DrRacket. If no information is available for a given key, the result should be the given default value.
See also module->language-info and racket/language-info.
procedure
(module-compiled-cross-phase-persistent? compiled-module-code)
→ boolean? compiled-module-code : compiled-module-expression?
14.4.3 Dynamic Module Access
procedure
(dynamic-require mod provided [fail-thunk]) → (or/c void? any/c)
mod :
(or/c module-path? resolved-module-path? module-path-index?) provided : (or/c symbol? #f 0 void?) fail-thunk : (-> any) = (lambda () ....)
If provided is #f, then the result is #<void>, and the module is not visited (see Module Expansion, Phases, and Visits) or even made available (for on-demand visits) in phases above the base phase.
> (module a racket/base (displayln "hello"))
> (dynamic-require ''a #f) hello
When provided is a symbol, the value of the module’s export with the given name is returned, and still the module is not visited or made available in higher phases.
> (module b racket/base (provide dessert) (define dessert "gulab jamun"))
> (dynamic-require ''b 'dessert) "gulab jamun"
If the module exports provided as syntax, then a use of the binding is expanded and evaluated in a fresh namespace to which the module is attached, which means that the module is visited in the fresh namespace. The expanded syntax must return a single value.
> (module c racket/base (require (for-syntax racket/base)) (provide dessert2) (define dessert "nanaimo bar") (define-syntax dessert2 (make-rename-transformer #'dessert)))
> (dynamic-require ''c 'dessert2) "nanaimo bar"
If the module has no such exported variable or syntax, then fail-thunk is called; the default fail-thunk raises exn:fail:contract. If the variable named by provided is exported protected (see Code Inspectors), then the exn:fail:contract exception is raised.
If provided is 0, then the module is instantiated but not visited, the same as when provided is #f. With 0, however, the module is made available in higher phases.
If provided is #<void>, then the module is visited but not instantiated (see Module Expansion, Phases, and Visits), and the result is #<void>.
procedure
(dynamic-require-for-syntax mod provided [ fail-thunk]) → any mod : module-path? provided : (or/c symbol? #f) fail-thunk : (-> any) = (lambda () ....)
procedure
(module-declared? mod [load?]) → boolean?
mod :
(or/c module-path? module-path-index? resolved-module-path?) load? : any/c = #f
If load? is #t and mod is not a resolved module path, the module is loaded in the process of resolving mod (as for dynamic-require and other functions). Checking for the declaration of a submodule does not trigger an exception if the submodule cannot be loaded because it does not exist, either within a root module that does exist or because the root module does not exist.
procedure
(module->language-info mod [load?])
→ (or/c #f (vector/c module-path? symbol? any/c))
mod :
(or/c module-path? module-path-index? resolved-module-path?) load? : any/c = #f
procedure
(module->imports mod)
→
(listof (cons/c (or/c exact-integer? #f) (listof module-path-index?)))
mod :
(or/c module-path? module-path-index? resolved-module-path?)
procedure
(module->exports mod)
→
(listof (cons/c (or/c exact-integer? #f) list?)) (listof (cons/c (or/c exact-integer? #f) list?)) mod : (or/c module-path? resolved-module-path?)
procedure
(module-predefined? mod) → boolean?
mod : (or/c module-path? resolved-module-path?)