Open Manual

Modules

Modules define exported names and public boundaries. A file can be both a

script and a module: imports see exported names, while direct execution runs

top-level statements.

File shape

use std.core

module stats(mean, median)

fn mean(list<number> xs) number {
   mut number total = 0
   for x in xs { total += x }
   total / xs.len
}

fn median(xs) number {
   xs.get(xs.len / 2)
}

Imports come first, the module declaration names the public surface, and helper

functions follow.

The module declaration defines the import surface. Names missing from the

export list are not public API.

Export lists

module stats(mean, median)

fn mean(xs) number { 0 }
fn median(xs) number { 0 }

Only exported names are part of the module surface. Helper names can remain

private by staying out of the export list.

Export lists document and enforce the module surface. A new public API name is

visible because it must be added to the list.

Public export names describe the API role. Helper names, migration aliases,

and local experiment names stay out of the public surface unless they are

intentionally supported.

Broad export

module tools *

Broad export fits small local modules and generated surfaces. Public library

modules use explicit export lists when the public surface needs to stay clear.

Broad export exposes all helpers in the file. Use it for local code and

generated tables; switch to an explicit list before the module becomes part of

a public namespace.

Grouped modules

module pkg.name {
   export core(run, stop)
   export debug(dump_state)
   internal(_state, _helper)
}

Grouped module declarations document public and internal regions explicitly.

They fit larger files where a single flat export list is hard to scan.

core is the default export profile. Named profiles are imported with

use module.path:profile:

use pkg.name:debug

The import makes core exports and the selected profile exports visible.

Private helpers

Private helper names are implementation details. A _ prefix is a convention,

not a substitute for an export list:

module text(clean)

fn _trim_edges(s) str { s }

fn clean(s) str {
   _trim_edges(s)
}

Importers see clean, not _trim_edges.

Private helpers can use narrower types when the public function validates the

input shape before calling them.

Script checks

Direct-run checks can live beside module declarations:

module math_extra(double)

fn double(int x) int {
   x * 2
}

#main {
   assert(double(4) == 8, "double")
}

When imported, double is visible. When run directly, the #main block also

executes.

Use module self-checks for cheap invariants that protect public APIs. Keep

external services, timing assumptions, private files, and large fixtures in

focused tests or examples.

Generated modules

Compile-time generation can emit module declarations:

module generated.api generated from Spec {
   emit make_api(Spec)
}

Generated modules still expose a documented public surface.

Publication check

docs are regenerated?

Common revisions

FindingRevision
Export list includes _helperKeep helper private or rename it as public API.
Module imports itself to reach helpersCall local helpers directly.
Script check performs slow IOMove it to a focused test or example.
Generated module lacks docsAdd docstrings to emitted public functions.
Many unrelated exportsSplit the file by domain before the surface becomes hard to scan.

Related