Status
Accepted
Context
Slug needs a clear, script-friendly execution model that also supports modular reuse.
We want:
- “Script ergonomics”: running a module should execute its top-level statements naturally.
- “Library ergonomics”: modules can perform initialization when imported (e.g., registration), but should not accidentally run the program entrypoint.
- A single, explicit entrypoint hook (
@main) that receives parameters using the same default-parameter semantics as all other Slug functions.
Decision
Module top-level execution
- Importing a module executes that module’s top-level statements.
- A module’s top-level statements execute at most once per process per canonical module identity (normal module caching rules).
- Top-level execution may transitively import other modules; those imports execute their top-level statements first ( depth-first as encountered).
@main execution
@mainnever executes on import.-
When a module is the running module (the root module passed to the runner, e.g.
slug path/to/mod.slug), the runtime executes: 1) the running module’s top-level statements, then 2) the running module’s@mainfunction (if present) - If top-level execution raises an error/exception,
@mainis not executed.
@main arity and parameter defaults
@mainis valid only on a function that is callable with zero arguments, using the standard Slug default-parameter semantics (no special rules for@main).- This allows patterns such as:
@main
fn start(args = argv(), param = cfg("param", 10)) {
// ...
}
- If the annotated function cannot be called with zero args, compilation fails.
Uniqueness and validation
- A module may define zero or one
@mainfunction. - If a module defines multiple
@mainfunctions, compilation fails. @mainmay only annotate functions (not structs/vals/modules).
Consequences
Positive
- Script-friendly: running a module executes its top-level code naturally.
- Explicit entry hook:
@mainprovides a clear “start here” function for the running module. - No special-case defaults:
@mainuses the same parameter-default rules as all other functions, keeping the language consistent. - Import safety vs entry: importing a module will not accidentally start the program (no
@mainon import), while still allowing module initialization at import-time.
Negative
- Import side effects: since imports execute top-level statements, importing can trigger I/O or other side effects. Tooling that loads modules must account for this.
- Tooling complexity: doc/test/indexing tools may require a future “load without executing top-level” mode or conventions that keep import-time side effects minimal.
Neutral
- Two-phase root execution (top-level then
@main) is a deliberate model:- top-level is suited to wiring/registration,
@mainis suited to CLI args/config-driven start-up.
- Future ADRs may refine:
- recommended conventions for keeping import-time side effects small,
- optional runner/tooling flags for “no top-level execution” in analysis contexts.