A module is a global scope that maps from names to definitions. The outermost of the nested sequence of scopes is always a module. Unlike a local scope, definition names in a module never have hygienic context.
The name of a module has a constant definition as a member of the class module. A top-level module's definition is in the modules module. A "nested" module's definition is in some other module.
A module has no parent scope, but when lookup does not find a name directly defined in a module, it tries the module's imports in the order they were specified. Each import specifies a module and either a name in that module or a pattern string that uses asterisks as wildcards. An import with a name makes that particular name's definition visible. An import with a pattern makes visible the definition of every name exported by the other module that matches the pattern.
Import does not support renaming, so a definition has the same name in every module where it is visible. Of couse you can always
def newname = module.oldnameto get the effect of renaming.
A module has a set of exported names. This is only used by import with a pattern.
A module knows its own name, for debugging and to allow finding a referenced module when loading a "fasl file" produced by separate compilation.
The slots of a module are accessed using function calls, because the . operator has a special meaning when its left-hand parameter is a module. Its result is the value of the definition visible in that module named by the right-hand parameter.
You can define a module using the defmodule statement, whose syntax is
$defmodule { module_path_name & "." }* name { ^ ( "export:" { export_name & "," }+ | "import:" { { import_path_name "." }* ( import_name | import_string ) & "," }+ | "local: " { ( local_name | local_string ) & "," }+ | "no_default_import" ) }*The result is the module object.
Except for looking up the prefix operator defmodule, nothing in a defmodule statement depends on the current module.
If no module_path_names are specified, the name of the module is name in the modules module. Otherwise the module_path_names start in the modules module and the name of the module is name in the last module in that path.
Each of the export_names is exported from the module that is being defined.
If no import_path_names are specified, the import_name or import_string refers to exports from the modules module. Otherwise the import_path_names start in the modules module and import from the last module in that path.
Thus import: lunar.defclass imports defclass from the lunar module and import: lunar."def*" imports from the lunar module every exported name that starts with "def".
import: module_name is useful because it allows the syntax module_name.definition_name to be used to refer to definitions in the module named module_name without importing them into the current module and risking name clashes.
local: is followed by one or more names or patterns. Any matching name will never be imported from another module.
Unless no_default_import is specified, import: lunar."*" is appended to the module definition, to import all the names defined by the Lunar language and exported from the lunar module.
The module class could have been defined by
defclass module(module_name name) global_scope, basic_name_context imports = stack[import_specifier]() reader: module_imports exports = stack[name]() reader: module_exports locals = stack[name | string]() reader: module_locals name = module_name name reader: module_name constant: sealed: defclass import_specifier(module module, pattern name | string) ;; TODO should this only allow access to exports of the specified module? def \.(m module, n name) scope_definitions(m)[n, default: false] or search_imports(m, n) or error("$n is not defined in $(module_name(m))") def search_imports(m module, n name) importable?(m, n) and for spec in module_imports(m) using return if spec.pattern eq n return \.(spec.module, n) else if spec.pattern in string and asterisk_match?(n.spelling, spec.pattern) and n in module_exports(spec.module) return \.(spec.module, n) def importable?(m module, n name) for local in module_locals(m) using never never local eq n if local in string never asterisk_match?(n.spelling, local) def asterisk_match?(value string, pattern string) ;; true if value = pattern with asterisks replaced by appropriate substrings
The module:, import:, and export: modifiers are recognized at top level.
module: is followed by an expression whose result is a module, which becomes the current module for parsing subsequent code in the source file. The expression is parsed in the modules module unless it starts with defmodule. The expression is typically a module name, a path of alternating names and dots to a module explicitly defined inside another module, or a defmodule statement.
import: is followed by a path of alternating names and dots, parsed in the modules module. The last name can be a string, which is a pattern that uses asterisks as wildcards. The imported name or names become visible in the current module.
export: is followed by a name or a definition, parsed in the current module. The name or the definition's name is exported from the current module. export: defclass exports all names defined by that statement, except for names other than the class name that start with an underscore character.
The following modules are built-in:
Module Name | Exports |
---|---|
lunar | names defined by the language |
compiler | additional names useful for writing macros and parsers |
modules | all modules not explicitly defined inside another module |
There can be other modules defined by libraries that have been loaded.
Previous page Table of Contents Next page