Modules & Import Loader Semantics
Relevant source files
The following files were used as context for generating this wiki page:
Introduction
In the Jazz programming language, the module system provides a structured way to organize code into hierarchical namespaces. The system is designed to support both explicit module declarations and automatic loading of common utilities via a bundled prelude. The current implementation focuses on dot-terminated source forms and brace-bodied module declarations, while full module and import loader semantics remain a primary aspirational goal for the compiler's development.
The active development of these semantics is centralized in the jazz-next/ directory, which serves as the canonical path for new compiler behavior. This system is responsible for managing the scope of bindings, resolving external references, and handling the interface between high-level Jazz modules and the underlying LLVM backend.
Sources: README.md:14-30, jazz-next/README.md:3-5, AGENTS.md:10-14
Module Structure and Syntax
Jazz modules are defined using a hierarchical naming convention and brace-enclosed bodies. This allows for deep nesting of logic, similar to package structures in other functional or object-oriented languages.
Hierarchical Namespaces
Modules use a double-colon (::) separator to define their position within the project's hierarchy. For example, a module might be declared as module Person::Organs::Heart. This structure maps conceptually to directory paths or nested class structures in other environments.
Sources: README.md:144-148
Declaration Syntax
The core syntax for modules includes:
- Module Keyword: Identifies the start of a module block.
- Brace-bodied Declarations: Modules encapsulate their internal bindings within curly braces
{ ... }. - Dot-terminated Forms: Source forms within or outside modules are typically terminated with a dot (
.).
Sources: README.md:23-23, README.md:144-148
Import Loader and Prelude
The Jazz compiler implements a specialized loader responsible for resolving names and bringing external functions into the local scope.
Bundled Prelude
By default, the jazz-next/ compiler loads a bundled prelude in CLI mode. This prelude provides a standard set of runtime helpers that are available to all Jazz programs without explicit imports.
| Helper | Description |
|---|---|
print! | Impure function for outputting text to the console. |
map | Higher-order function for transforming collections. |
filter | Higher-order function for selecting elements from collections. |
hd | Retrieves the head of a list. |
tl | Retrieves the tail of a list. |
Sources: README.md:21-21, README.md:65-68
Module Resolution Flow
The resolution process involves checking local bindings, followed by the prelude, and eventually navigating the module graph. In the active jazz-next/ implementation, the module graph and resolver are validated through specific test suites.
The diagram shows the sequential flow from initial compilation to the resolution of names via the prelude and the internal import loader.
Sources: jazz-next/README.md:32-32, README.md:65-70
Compiler Architecture for Modules
The jazz-next compiler architecture separates the parsing of module syntax from the semantic analysis of the module's contents.
Key Components
- Parser: Handles the surface syntax of module declarations and dot-terminated forms.
- Analyzer: Performs scope-aware rebinding analysis and binding/signature coherence checks.
- Resolver: Manages the module graph and identifies the location of external definitions.
| File Path | Responsibility |
|---|---|
src/JazzNext/Compiler/Parser.hs | Surface parsing of module blocks and declarations. |
src/JazzNext/Compiler/Analyzer.hs | Scope analysis and binding/signature coherence. |
test/JazzNext/Compiler/Modules/ | Tests for prelude loading, module graph, and resolver. |
Sources: jazz-next/README.md:13-32
Backend Module Representation (LLVM)
At the backend level, Jazz modules are mapped to LLVM modules. The historical reference code in jazz-hs/ illustrates how Jazz manages these lower-level constructs through an FFI layer.
Module Metadata and Identification
The compiler tracks module identities and source file origins to facilitate debugging and linking. This is handled via the LLVM Module class, accessed through C++ FFI wrappers.
- Module Identifier: A unique string identifying the module within the LLVM context.
- Source File Name: Tracks the original Jazz source file that generated the LLVM module.
- Named Metadata: Used to store module-level information that persists through the optimization phases.
Sources: jazz-hs/local-deps/llvm-hs/src/LLVM/Internal/FFI/ModuleC.cpp:14-25, jazz-hs/local-deps/llvm-hs/src/LLVM/Internal/FFI/ModuleC.cpp:52-54
Just-In-Time (JIT) Loading
For interactive CLI use, Jazz utilizes the OrcJIT (Version 2) to load modules dynamically. This involves creating a JITDylib for the session and adding modules to an IRLayer.
The sequence diagram illustrates the process of initializing a JIT environment and loading a compiled module for execution.
Sources: jazz-hs/local-deps/llvm-hs/src/LLVM/Internal/FFI/OrcJITC.cpp:38-40, jazz-hs/local-deps/llvm-hs/src/LLVM/Internal/FFI/OrcJITC.cpp:115-117, jazz-hs/local-deps/llvm-hs/src/LLVM/Internal/FFI/OrcJITC.cpp:143-155
Conclusion
The module and import loader semantics in Jazz provide a foundation for scalable code organization and runtime utility access. By combining high-level hierarchical declarations with a default prelude and a robust backend integration via LLVM, Jazz ensures that name resolution is consistent across both compiled and JIT-executed code. Future development in jazz-next/ continues to refine the full semantics of the import loader to support more complex dependency graphs.
Sources: README.md:23-28, jazz-next/README.md:3-7