Modules and Packages¶
Spectre provides a module system for organizing code into reusable units. This document covers module imports, visibility modifiers, and package organization.
Module Imports¶
Modules are imported using the use statement. The use statement loads a module and binds it to a variable for use in the current scope.
Basic Import Syntax¶
val module_name = use("module_path")
Importing the Standard Library¶
The standard library is imported using the use statement:
val std = use("std")
Once imported, the standard library’s functionality is accessible through the std binding:
val std = use("std")
pub fn main() void! = {
std.io.print("Hello, world.")
}
Importing Specific Files¶
Specific source files can be imported by providing the file path:
val some_other_module = use("some_other_module.spr")
Module Binding¶
The module is bound to a val binding, which means the binding itself is immutable:
val std = use("std")
// std = something_else // Error: cannot reassign immutable binding
Visibility Modifiers¶
Spectre uses the pub keyword to control visibility of functions and constants.
Public Functions¶
Functions marked with pub are visible outside their module:
pub fn main() void! = {
// Entry point - visible to runtime
}
pub fn add(x: i32, y: i32) i32 = {
return x + y
}
Private Functions¶
Functions without the pub keyword are private to their module:
fn helper_function() void = {
// Only visible within this module
}
pub fn public_function() void = {
helper_function() // Valid: called within same module
}
Public Constants¶
Constants can be marked as public:
pub val some_constant = 1000
Visibility Summary¶
Modifier |
Scope |
|---|---|
pub |
Visible outside the module |
(none) |
Private to the module |
Module Structure¶
A typical Spectre module follows this structure:
// 1. Module imports
val std = use("std")
val other_module = use("other_module.spr")
// 2. Type definitions
type Point = {
x: mut i32
y: mut i32
}
// 3. Private functions and constants
val private_constant = 100
fn helper_function() void = {
// Implementation
}
// 4. Public API
pub val public_constant = 1000
pub fn some_function(some_arg: i32, some_other_arg: usize) void = {
pre {
is_bigger_than_ten : some_arg > 10
is_bigger_than_twenty : some_other_arg > 20
}
post {
operation_complete : true
}
// Implementation
}
pub fn main() void! = {
some_function(15, 25)
}
Standard Library¶
The Spectre standard library provides common functionality for I/O, data structures, and utilities.
Importing Standard Library¶
val std = use("std")
Standard Library I/O¶
The standard library provides I/O functionality through the io module:
val std = use("std")
pub fn main() void! = {
std.io.print("Hello, world.")
std.io.put_any("{d} {d}", {x, y})
}
I/O Functions¶
Based on the sample code, the standard library provides:
``print``: Print a string message
``put_any``: Print formatted output with placeholders
// Print a simple string
std.io.print("Hello, world.")
// Print formatted output
std.io.put_any("{d} {d}", {x, y})
Cross-Module Calls¶
Functions from other modules can be called after importing:
val std = use("std")
val utils = use("utils.spr")
pub fn main() void! = {
// Call standard library function
std.io.print("Starting...")
// Call function from utils module
utils.process_data()
}
Trust and Modules¶
When importing modules, trust markers must be respected across module boundaries.
Trusted Module Functions¶
Functions from imported modules that are marked as trusted must be handled appropriately:
val std = use("std")
// Trusted function from standard library
pub fn main() void! = {
std.io.print("This is trusted")
}
Using Trust with Module Calls¶
val std = use("std")
fn pure_function() void = {
trust std.io.print("This is trusted now")
}
Examples¶
Basic Module Structure¶
val std = use("std")
type Point = {
x: mut i32
y: mut i32
}
pub fn main() void! = {
std.io.print("Hello, world.")
}
Multi-Module Program¶
main.spr:
val std = use("std")
val math = use("math.spr")
pub fn main() i32 = {
val result = math.add(1, 2)
std.io.print("Result computed")
return 0
}
math.spr:
pub fn add(x: i32, y: i32) i32 = {
return x + y
}
test {
assert add(1, 2) == 3
}
Module with Contracts¶
val std = use("std")
pub fn trusted_function(x: i32, y: i32) i32 = {
pre {
is_above_zero : x > 0 && y > 0
}
post {
x < 100 && y < 100
}
return x + y
}
pub fn main() void = {
std.io.put_any("{d}", {trusted_function(1, 2)})
}
Best Practices¶
Module Organization¶
Group related functionality: Keep related functions and types in the same module
Minimize public API: Only expose what is necessary
Use descriptive names: Module names should reflect their purpose
Document dependencies: Clearly indicate which modules depend on others
Import Conventions¶
Standard library first: Import
stdbefore other modulesConsistent naming: Use consistent names for common modules (e.g.,
stdfor standard library)Explicit paths: Use explicit file paths for local modules
Visibility Guidelines¶
Default to private: Keep functions private unless they need to be public
Public API documentation: Document all public functions and types
Minimize trusted code: Use contracts wherever possible, even in public APIs
File Organization¶
Spectre source files use the .spr extension. A typical project structure:
project/
├── main.spr # Entry point
├── utils.spr # Utility functions
├── types.spr # Type definitions
└── std/ # Standard library
└── io.spr # I/O functionality
Summary¶
Spectre’s module system provides:
``use`` statement: Import modules with
val name = use("path")``pub`` modifier: Control visibility of functions and constants
Standard library: Access via
val std = use("std")Private by default: Functions are private unless marked
pub
Key points:
Modules are imported using
val name = use("path")The standard library is imported as
stdPublic items are marked with
pubPrivate items are only visible within their module
Trust markers propagate across module boundaries
For information on testing modules, see the Testing documentation.