Note: Every program on this page was executed against the 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.5You 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.
Choosing an output path
topaz run executes through the interpreter — the reference path, and all you need for the loop above. When you want a compiled artifact, the same source lowers through two backend targets.
Rust — a native binary. topaz build lowers hello.tpz to Rust and builds a stand-alone executable:
topaz build hello.tpz --out-dir out --runIt builds out/target/debug/program offline and (--run) executes it, printing the same Hello, Topaz!. This target needs a Rust toolchain present, as noted above.
Python — generated source. topaz build --target python emits Python instead, with no Rust toolchain involved:
topaz build hello.tpz --target python --out-dir py-outIt writes program.py plus its runtime topaz_py_rt.py. Both backends are checked against the interpreter; the measured-surface evidence behind each is published on the status page.
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