On this page:
1.2.1 Phases
1.2.2 Module Redeclarations
1.2.3 Submodules

1.2 Modules and Module-Level Variables🔗ℹ

Most definitions in Rhombus are within modules. In terms of evaluation, a module is essentially a prefix on a defined name, so that different modules can define the same name. That is, a module-level variable is like a top-level variable from the perspective of evaluation.

One difference between a module and a top-level definition is that a module can be declared without instantiating its module-level definitions. Evaluation of a import instantiates (i.e., triggers the instantiation of) the declared module, which creates variables that correspond to its module-level definitions.

For example, given the module declaration

// in "m.rhm"

#lang rhombus

def x = 10

the evaluation of import "m.rkt" creates the variable x and installs 10 as its value. This x is unrelated to any top-level definition of x (as if it were given a unique, module-specific prefix).

1.2.1 Phases🔗ℹ

The purpose of phases is to address the necessary separation of names defined at evaluation time versus names defined at expansion time.

A module can be instantiated in multiple phases. A phase is an integer that, like a module name, is effectively a prefix on the names of module-level definitions. Phase 0 is the run-time phase.

A top-level import instantiates a module at phase 0, if the module is not already instantiated at phase 0. A top-level import meta instantiates a module at phase 1 (if it is not already instantiated at that phase); meta also has a different binding effect on further program parsing, as described in Expansion Binding.

Within a module, some definitions are already shifted by a phase: the meta form shifts expressions and definitions by a relative phase +1. Thus, if the module is instantiated at phase 1, the variables defined with meta are created at phase 2, and so on. Moreover, this relative phase acts as another layer of prefixing, so that x defined with def and x defined with meta def can co-exist in a module without colliding. A meta form can be nested within a meta form, in which case the inner definitions and expressions are in relative phase +2, and so on. Higher phases are mainly related to program parsing instead of normal evaluation.

If a module instantiated at phase n imports another module, then the imported module is first instantiated at phase n, and so on transitively. (Module imports cannot form cycles.) If a module instantiated at phase n imports another module M with meta, then M becomes available at phase n+1, and it later may be instantiated at phase n+1. If a module that is available at phase n (for n>0) imports another module M with meta -1, then M becomes available at phase n-1, and so on. Instantiations of available modules above phase 0 are triggered on demand as described in Module Expansion, Phases, and Visits.

A final distinction among module instantiations is that multiple instantiations may exist at phase 1 and higher. These instantiations are created by the parsing of module forms (see Module Expansion, Phases, and Visits), and are, again, conceptually distinguished by prefixes.

Top-level variables can exist in multiple phases in the same way as within modules. For example, def within meta creates a phase 1 variable. Furthermore, reflective operations like Evaluator.make_rhombus and eval provide access to top-level variables in higher phases, while module instantiations (triggered by import) relative to such top-levels are in correspondingly higher phases.

1.2.2 Module Redeclarations🔗ℹ

When a module is declared using a name with which a module is already declared, the new declaration’s definitions replace and extend the old declarations. If a variable in the old declaration has no counterpart in the new declaration, the old variable continues to exist, but its binding is not included in the lexical information for the module body. If a new variable definition has a counterpart in the old declaration, it effectively assigns to the old variable.

If a module is instantiated in the current namespace’s base phase before the module is redeclared, the redeclaration of the module is immediately instantiated in that phase.

If the current inspector does not manage a module’s declaration inspector (see Module Registry), then the module cannot be redeclared. Even if redeclaration succeeds, instantiation of a module that is previously instantiated may fail if instantiation for the redeclaration attempts to modify variables that are constant.

1.2.3 Submodules🔗ℹ

A module form within a module declares a submodule. A submodule is accessed relative to its enclosing module, usually with the ! operator. Submodules can be nested to any depth.

Although a submodule is lexically nested within a module, a subsmodule declared with ~lang cannot access the bindings of its enclosing module directly. In that case, unless a submodule imports from its enclosing module or vice versa, then visits or instantiations of the two modules are independent, and their implementations may even be loaded from a compiled form at different times.

See module for more information.