Find a syntax example here and follow its manual link for the detailed rules. If you are new to Topaz, start with First Program and the Learning path, then keep this page nearby as a reference.
The everyday working set
Browse the cards to see the main forms, or jump to the one you need. The module and project-command cards link to the files they require.
Values, bindings, and types
Give values names and choose which names can be reassigned.
const DEFAULT_MINUTES = 25
let title: string = "Check program"
let mut completed: int = 0
completed = completed + 1completed becomes 1. const needs a value known during checking. let binds a name without reassignment, while let mut allows reassignment. Write : Type to state the type, or let the checker infer it.
Shape let name: Type = expression
Manual Types
Functions and lambdas
Reuse a calculation in a named function, or pass a short function as a value.
function double(x: int) -> int {
x * 2
}
let increment: (int) -> int = x => x + 1
print("{double(increment(3))}")This prints 8. increment holds a lambda, a function written without a name. double declares its input and return types and returns its last expression. You can also use return.
Shape function name(value: Type) -> Result { expression } and value => expression
Manual Functions & Generics
Operators and expression results
Use the last expression in a block as its result.
let total = {
let base = 20
base + 5
}
print("{total}")This prints 25. Blocks, if, match, and for used as a value can return results. Assignment is a statement and cannot serve as the final result.
Shape { statements; finalExpression }
Manual Operators & Expressions
if, match, and for
Choose a branch with if or match, and collect results with for.
let scores = [92, 74]
let labels = for score in scores {
match score {
case 90..100 => "A"
case _ => if score >= 70 {
"pass"
} else {
"retry"
}
}
}labels is ["A", "pass"]. The for expression collects one result for each score into an Array<string>. if tests a Boolean condition, and match selects a branch from patterns.
Shape if condition { value } else { value }, match value { case pattern => value }, and for item in values { value }
Manual Patterns & Control Flow
Arrays, maps, and sets
Store ordered values, look up a value by key, or remove duplicates.
let scores: Array<int> = [80, 90]
let ids = set { 3, 1, 3 }
let labels = map { 1: "one", 2: "two" }
print("{scores}:{ids.toArray()}:{labels.keys}")This prints [80, 90]:[3, 1]:[1, 2]. Array<T> keeps values in index order. Map<K, V> looks up values by key, and Set<T> stores each value once. Maps and sets keep insertion order. Repeating a key in a map literal is an error.
Shape [a, b], map { key: value }, and set { a, b }
Manual Collections & Comprehensions
Records, enums, and unions
Group fields in a record, list alternatives in an enum, or combine types with a union.
record Task {
title: string,
done: bool = false,
}
enum Filter {
All,
Open,
}
type Selection = Task | null
let selected: Selection = nullselected has no task because its value is null. Task groups named fields, Filter lists its possible variants, and Task | null accepts either type. Records and enums are nominal types, so separate declarations define different types even when their fields match.
Shape record Name { fields }, enum Name { variants }, and type Choice = A | B
Manual Records & Nominal Data
Absence, recoverable failure, faults, and cleanup
Return an error that the caller can handle and close an opened file when the function exits.
function readPlan(path: string) -> Result<string, string> {
let file = open(path)?
defer { file.close() }
file.read()
}Calling readPlan returns the file contents or an error in Result. ? extracts an Ok value or returns an Err to the caller. defer closes the file when the scope ends, including an early return. For an expected missing value, use Option<T> with Some or None. A runtime fault, such as an invalid array index, stops execution and cannot be caught with ?.
Shape Some(value) / None, Ok(value) / Err(error), result?, and defer { cleanup }
Strings and templates
Insert values into a string or keep a path as a template.
let name = "Topaz"
let greeting = "Hello, {name}!"
let path = p"notes/{name}.txt"greeting is "Hello, Topaz!". The p tag creates a path template. The other built-in tags are r for regular expressions, sh for shell commands, and sql for parameterized SQL. Creating a template does not execute its host operation. Backtick templates, dollar-brace interpolation, HTML tags, and user-defined tags are not supported.
Shape "text {value}" and tag"text {value}"
Manual Strings & Templates
Modules and visibility
Import declarations from another file. This excerpt assumes that src/plan.tpz exports StudyTask and summarize. For the full files, follow First Application.
import src.plan { StudyTask, summarize }
export function summary(tasks: Array<StudyTask>) -> string {
summarize(tasks)
}Each .tpz file is a module. Put imports at the top of the file. A dotted path starts at the compilation root, so src.plan names src/plan.tpz. Other modules can import declarations marked export. This import brings in the two named declarations and leaves private declarations inaccessible.
Shape import module.path { Name } and export function name(...)
Manual Modules & Visibility
Format, check, test, run, and build
Use these commands while working on a package. The examples assume a package manifest, a lock file, and tests/plan.tpz in the working folder. Create them by following First Application.
topaz fmt --check --root .
topaz check --root . --locked
topaz test tests/plan.tpz --root . --locked
topaz run --root . --locked
topaz build --release --root . --locked --out-dir buildfmt --check reports formatting differences and leaves files unchanged. check reports source errors without executing the program. test reports test results, and run prints the program output. build writes the native executable and its supporting files to build. The native build needs Rust with Cargo for your platform. Each command exits with code 0 on success. The linked lesson shows the expected program output.
Shape topaz <task> [inputs]
Manual CLI & Diagnostics
Advanced routes
Choose a topic when your program needs it.
- Use Pipelines & Placeholders to pass values through steps from left to right.
- Use Collections & Comprehensions to build collections from nested loops and conditions.
- Use Records & Nominal Data to define protocols that check whether a type provides the required members.
- Use Functions & Generics to write a function with a type parameter.
- Use Concurrency to run named tasks together with a timeout.
- Use Files & Resources to read files and close resources.
- Use WASM & Playground to build a WASM package called from JavaScript.
- Use Core Library for bytes, buffers, hashes, and codecs.
To build your first application step by step, return to the Learning path. Work through values and functions, data and control, then failures and resources before the first application.