22. Modules and Visibility
22.1 Import Syntax
-- Import entire module (qualified access)
import math;
println(math.square(5)); -- 25
-- Import with alias
import std.strings as strs;
strs.quotes("hi") println; -- "hi"
-- Import all exports into current scope (wildcard)
import std.strings.*;
"7" padStart(3, "0") println; -- 007, no qualifier needed
-- Import specific names
import std.strings.{glob, padStart};
"kick.wav" glob("*.wav") println;
-- Import with aliases
import std.strings.{padStart as padLeft};
"7" padLeft(3) println;
General-purpose standard-library modules live under the std.* namespace (std.strings, std.path, std.fs, std.result, std.test, std.json, std.message, std.thunk, std.futures). Math, string, and collection functions are built-ins — always available, no import needed. Audio-domain modules (synthdef, common_ugens, filters, dsp_math) keep flat names. See the Standard Library reference.
22.2 Dotted Paths
-- Dotted paths map to directories:
-- std.strings → std/strings.x
-- utils → utils.x
import std.strings; -- loads std/strings.x
strings.quotes("hi") println; -- qualified by last path component
22.3 File Resolution
Module Search Order
When resolving an import, the compiler searches in this order:
- Relative to the directory containing the importing file
- Each directory in the CLI
-I path1:path2argument - Each directory in the
TZPL_PATHenvironment variable (colon-separated) - The auto-discovered standard library (
$TZPL_HOME/modulesif set, else themodules/directory found next to the executable, else the source tree'slang/modules)
Run with include paths:
./tzpl -I lib:vendor main.x
Or set the environment variable:
export TZPL_PATH=/usr/local/lib/tzpl:/home/user/libs
22.4 Visibility and Exporting
-- By default, all top-level declarations are exported
fn add(a Int, b Int) Int = a + b; -- exported
const PI = 3.14159; -- exported
struct Point { x Float, y Float } -- exported
-- Two ways to make a declaration private (not exported):
-- 1. The private keyword
private fn helper(x Int) Int = x * 2;
-- 2. Underscore prefix
fn _internal(x Int) Int = x + 1;
Private names cannot be accessed by importers:
import utils.*; _internal(5); -- compile error
import utils; utils.helper(5); -- compile error
Imported names are NOT re-exported by default.
If module A imports from module B, importers of
A do not see B's names. Use export
(section 21.5) to opt in to re-exporting.
22.5 Re-exporting Imports with export
export is a drop-in replacement for import:
the grammar is identical, but every name pulled in by an
export statement is also added to this module's export
table. The two keywords can be mixed freely within a module.
-- Plain `import`: symbols are local to this module only.
import math_utils.*;
-- `export`: symbols are local AND visible to importers of this module.
export math_utils.*;
-- Re-export only selected names.
export math_utils.{square, cube};
-- Re-export with renaming.
export math_utils.{square as sq};
-- Re-export the module alias so importers can use math_utils.square(...).
export math_utils;
-- Re-export a dotted path or renamed alias.
export std.strings as strs;
A downstream module then sees the re-exported names as if they were declared locally in the re-exporter:
-- File: math_bridge.x
export math_utils.*; -- re-exports square, cube, ...
fn quadruple(x Int) Int = x * 4; -- local (and exported by default)
-- File: main.x
import math_bridge.*;
5 square println; -- 25 (from math_utils, via math_bridge)
2 quadruple println; -- 8 (local to math_bridge)
Shadowing
A local declaration with the same name as an imported one always wins. The re-export is silently dropped so the local definition is the sole exported binding:
export math_utils.*; -- math_utils defines `square`
fn square(x Float) Float = x * x; -- local square shadows the import
-- Importers of this module see only the local `square`.
Chained access through a re-exported alias
Given export math_utils; in module A, the
alias math_utils is only reachable via a wildcard or named
import of A. Writing A.math_utils.square(5)
through a whole-module import (import A;) is rejected:
import A;
A.math_utils.square(5); -- compile error: use `import A.*` or `import A.{math_utils}`
22.6 Circular Import Detection
Circular imports are detected and reported as errors.
If a.x imports b.x and b.x imports a.x, the compiler
will report: "Circular import detected: a"