Extending the Built-in Prelude
Relevant source files
The following files were used as context for generating this wiki page:
- README.md
- jazz-next/README.md
- jazz-hs/package.yaml
- jazz-next/scripts/test-warning-config.sh
- docs/spec/runtime/primitive-semantics.md (referenced via scripts/check-clarification-specs.sh)
- docs/spec/syntax/operators.md (referenced via scripts/check-clarification-specs.sh)
The Jazz Prelude serves as the foundational runtime environment, providing essential functional helpers and impure side-effecting operations to every Jazz program by default. In the current jazz-next implementation, the prelude is bundled and loaded automatically during CLI execution, ensuring that common utilities are available without manual imports.
Extending the prelude involves adding or modifying core functional helpers (like map and filter) or impure "effect" functions (denoted by the ! suffix). Because the language emphasizes a distinction between pure and impure code, the prelude acts as the primary interface for both standard algorithmic logic and system-level interactions like printing or I/O.
Sources: README.md:14-18, README.md:50-54, jazz-next/README.md:31
Core Prelude Architecture
The prelude is integrated into the compilation and execution pipeline through the jazz-next driver and module resolver. It is treated as a specialized module that provides global scope to the user's code.
Component Breakdown
| Component | Responsibility |
|---|---|
| Bundled Helpers | Standard functions like map, filter, hd, and tl. |
| Impure Built-ins | Functions ending in ! (e.g., print!) that perform side effects. |
| Module Resolver | Logic in jazz-next that handles the loading of the prelude graph. |
| Builtin Catalog | A registry of names known to the compiler as intrinsic or primitive. |
Sources: README.md:14-18, jazz-next/README.md:21, jazz-next/README.md:31-33, jazz-next/scripts/test-warning-config.sh:37-38
Initialization Flow
The following diagram illustrates how the jazz-next compiler loads the prelude before processing user source files.
The jazz-next driver ensures that the prelude is present in the module graph before analysis begins, allowing user-facing helpers to be resolved from the prelude rather than being hard-coded compiler intrinsics.
Sources: README.md:50-54, jazz-next/README.md:21-23, jazz-next/README.md:31-33
Implementing New Built-ins
Extending the prelude requires updates across the parser, analyzer, and runtime semantics. Functions in Jazz are classified as pure or impure, and this distinction must be maintained when adding new members to the prelude.
Pure Functional Helpers
Pure functions are standard lambdas. Common extensions include collection transformations or mathematical operations. These are typically implemented using standard Jazz syntax within the prelude source and exported.
- Syntax:
name = \(args) -> body. - Currying: All prelude functions support partial application by default.
Impure Effect Functions
Functions that perform I/O or other side effects must use the ! suffix. The compiler enforces a "stub-v1" effect typing where impure functions cannot be called from pure binding bodies.
- Naming Convention: Must end with
!, such asgetLine!orprintln!. - Enforcement: Direct
!callee calls are checked in binding bodies to ensure purity.
Sources: README.md:19-20, README.md:52-54, README.md:118-124
Language Support and Semantics
New additions to the prelude must adhere to the defined primitive semantics and operator fixity rules.
Operator Extensibility
The prelude defines standard operators like $ (right-associative, 0 precedence) and +. Extending the prelude with new operators requires defining their fixity and precedence in the built-in operator table.
| Operator | Precedence | Associativity | Description |
|---|---|---|---|
$ | 0 | Right | Function application to avoid parentheses. |
+ | (Numeric) | Left | Standard addition. |
Sources: README.md:65-70, scripts/check-clarification-specs.sh:42-43
Primitive Semantics
Any extension that introduces new primitive types or fundamental operations (like equality) must conform to the project's "Primitive Contract Table." This includes defining the runtime failure model for the new operation.
The sequence shows the transition from the definition of a prelude function through its static analysis to its final runtime execution according to primitive contracts.
Sources: scripts/check-clarification-specs.sh:46-48, jazz-next/README.md:21-23
Testing Extensions
All prelude extensions are verified through a suite of semantic and module resolution tests within jazz-next.
- Prelude Loading:
test/JazzNext/Compiler/Modules/PreludeLoadingSpec.hsverifies that the prelude is correctly injected into the environment. - Builtin Catalog:
test/JazzNext/Compiler/Semantics/BuiltinCatalogSpec.hsensures that all new built-ins are correctly registered and accessible. - Purity Checks:
test/JazzNext/Compiler/Semantics/PuritySemanticsSpec.hsvalidates that!suffixes are correctly enforced.
Sources: jazz-next/README.md:31-38, jazz-next/scripts/test-warning-config.sh:35-38
Summary
Extending the Built-in Prelude is a multi-step process involving the registration of new symbols in the BuiltinCatalog, implementation of logic (pure or impure), and verification via the jazz-next test harness. By maintaining the strict separation of pure and impure functions (denoted by !), the prelude provides a safe and predictable foundation for Jazz applications while allowing for staged extensibility of the language's core utility set.