Note: Every program on this page was executed against the v5.2 toolchain exactly as shown, and the printed output is pinned byte-for-byte by the site verifier. See Toolchain status for what runs today and what proves it.
Install Topaz
Install the topaz toolchain with no repo clone and no build.
Shell (macOS / Linux):
curl -fsSL https://topaz.ooo/install.sh | shWindows (PowerShell):
irm https://topaz.ooo/install.ps1 | iexWith npm, if you already have Node:
npm install -g topaz-langThen confirm it:
topaz versionTopaz 5.2You can also try Topaz in your browser, with nothing to install.
One caveat about native builds. topaz run, topaz check, and topaz emit work from the standalone binary with nothing else installed. Compiling straight to a native binary with topaz build shells out to cargo through rustup, so that one command needs a Rust toolchain present on the machine.
Your first program
Create hello.tpz:
let greeting = "Hello, Topaz!"
print(greeting)Run it:
topaz run hello.tpzHello, Topaz!That is the whole loop: one file, topaz run, output. No project scaffolding, no manifest, no build step.
A ten-line tour
tour.tpz touches the core of the language: match with guards and ranges, a mutable Map, a higher-order function, and option fallback:
function classify(n: int) -> string {
return match n {
case 0 => "zero"
case x if x < 0 => "negative"
case 1..99 => "small"
case _ => "large"
}
}
let mut report: Map<string, string> = Map.new()
for n in [-5, 0, 42, 1000] {
report.insert("{n}", classify(n))
}
print("{report.keys}")
print("{map([1, 2, 3], (x: int) => x * x)}")
print("{toInt("42") ?? 0}")topaz run tour.tpz[-5, 0, 42, 1000]
[1, 4, 9]
42Three things to notice:
matchis an expression.classifyreturns its result directly. Arms take literals, ranges, bindings, andifguards."{…}"interpolation is the one way to format strings; it works on every value, includingreport.keys.toInt("42")returns an optional, and??supplies the fallback. Nothing converts silently.
When a program stops
An out-of-bounds index is not a recoverable error in Topaz. It is a fault: the program stops and the toolchain renders a diagnostic. fault.tpz:
let xs = [10, 20, 30]
let i = 7
print("{xs[i]}")topaz run fault.tpzerror[TPZ4001]: index 7 is out of bounds for an array of length 3
--> fault.tpz:3:9
|
3 | print("{xs[i]}")
| ^^^^^
The process exits nonzero. Faults mark contract violations; absence you expect to handle is modeled with optionals instead. See Error Handling.
Modules in two files
A directory of .tpz files is a unit. Files import each other by name; export is the only visibility control. util.tpz:
export function shout(s: string) -> string {
return s + "!"
}
export let greeting: string = "hello"main.tpz, in the same directory:
import util
print(util.shout(util.greeting))topaz run main.tpzhello!The resolver finds util.tpz next to the entry file: no manifest, no path configuration. The full rules (paths, collisions, cycles, initialization order) are in Modules.
Building a native binary
topaz run executes through the interpreter. To compile a program to a
stand-alone native executable instead, use topaz build:
topaz build hello.tpz --out-dir out --runIt lowers hello.tpz to Rust, builds it offline into out/target/debug/program,
and (--run) executes it, printing the same Hello, Topaz!. This is the path
that needs a Rust toolchain present; the status page tracks
the toolchain gates.
Next steps
- Syntax — the surface shape of the language
- Data Types — strings, numbers, collections, records
- Functions & Closures — the functional core
- Modules — multi-file units in depth
- Toolchain status — what runs today, and the gates that prove it