Split a program across files and choose which names other files can use. You will run a two-file study planner, then fix private-name and import-cycle errors.
Install topaz using First Program. Each .tpz file is a module. No separate module declaration is needed.
Run a program from two files
In a working folder without an existing study-plan project, run these commands.
topaz init --root study-plan
cd study-planinit creates topaz.toml and src/main.tpz. Keep the generated manifest. Add src/plan.tpz and replace src/main.tpz with the code below. After you generate the lock file, the layout is as follows.
study-plan/
├── topaz.toml
├── topaz.lock
└── src/
├── main.tpz
└── plan.tpzSave the task data and functions in src/plan.tpz.
export record StudyTask {
title: string,
minutes: int,
done: bool = false,
}
function parseMinutes(text: string) -> Result<int, string> {
match toInt(text) {
case Some(value) if value > 0 => Ok(value)
case _ => Err("minutes must be a positive integer")
}
}
export function makeTask(
title: string,
minuteText: string,
done: bool = false,
) -> Result<StudyTask, string> {
let minutes = parseMinutes(minuteText)?
Ok(StudyTask { title: title, minutes: minutes, done: done })
}
export function summarize(tasks: Array<StudyTask>) -> string {
let mut remaining = 0
for task in tasks {
if !task.done {
remaining = remaining + task.minutes
}
}
"tasks={tasks.length}, remaining={remaining} minutes"
}In src/main.tpz, import the three names used by the application.
import src.plan { StudyTask, makeTask, summarize }
export function main(args: Array<string>, stdin: string) -> Result<int, string> {
let tasks: Array<StudyTask> = [
makeTask("Run first program", "10", true)?,
makeTask("Build application", "25", false)?,
]
print(summarize(tasks))
Ok(0)
}Run these commands inside study-plan, beside topaz.toml.
topaz lock --root .
topaz check --root . --locked
topaz run --root . --lockedlock creates topaz.lock. The check exits with code 0, and the program prints the summary.
tasks=2, remaining=25 minutesThe path src.plan selects src/plan.tpz under the chosen root. The compiler checks the entry file and the modules it imports, following their imports too. These files form the compilation unit. Unrelated files are not checked for this entry.
Choose which names to export
A declaration is private by default. Add export to make it available to another module. Here, StudyTask, makeTask, and summarize are exported. parseMinutes stays private.
If src/main.tpz tries to import or access parseMinutes, checking fails. Call the exported makeTask instead. To expose the helper intentionally, add export to its declaration in src/plan.tpz. Imported bindings are read-only.
| Task | Import form |
|---|---|
| Select names | import src.plan { StudyTask, makeTask } |
| Rename a selected name | import src.plan { summarize as renderSummary } |
| Group names under a prefix | import src.plan as plan |
The last form creates a namespace, a prefix for looking up exported names during compilation. Call plan.makeTask(...) through it. The namespace itself is not a runtime value.
Use plan.StudyTask in a type annotation. To construct StudyTask { ... }, select StudyTask directly. The form plan.StudyTask { ... } is rejected. If you wrote import src.plan as plan { summarize }, replace it with one of the separate import forms above.
Find files from the right root
Match each dotted path to the exact file and folder names under the root. Put imports at the top of the file, before other items.
Package commands read the entry, dependencies, and build target from topaz.toml. With an explicit file argument, the entry file's folder is the default root. Use --root to select a containing folder. If src.plan cannot be found, check both --root and the location of src/plan.tpz.
Dependencies initialize once, before the module importing them. An imported module can contain declarations and bindings. Free top-level loops and assignments are rejected, so put that work inside a function and call it from the entry.
Break an import cycle
In a separate folder, save this as a.tpz.
import b
export function fromA() -> int {
1
}Save this as b.tpz beside it.
import a
export function fromB() -> int {
2
}In that folder, topaz check a.tpz --root . exits nonzero and reports the cycle.
error[TPZ3006]: import cycle: a -> b -> a
--> a.tpz:1:8
|
1 | import b
| ^
a.tpz: 1 diagnosticMove shared declarations from a.tpz and b.tpz into a third module, then import it from both files. Alternatively, remove the dependency in one direction. Every import participates in cycle detection, including imports used for types.
Forms the compiler rejects
The compiler rejects use, string or template paths, an alias combined with a selection list, and side-effect-only imports. It also rejects export lists, wildcard exports, re-exports, export-site renaming, and export let mut. Import paths cannot leave the root, and namespaces cannot expose private names.
Continue with First Application to add tests and build this program. Use Packages & Distribution for dependencies and locks, or Forbidden & Deferred Forms to compare familiar syntax with supported forms.