The examples on this page are checked and executed with the Topaz tools. See Toolchain Status for current target support.
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.6You 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 support file topaz_py_rt.py. Current support for both output paths is listed 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 — current tools, targets, and limits