Overview

Getting Started with Topaz

Install Topaz and run your first program with the v5.2 toolchain: hello, a ten-line tour, a real fault, and a two-file module unit.

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 | sh

Windows (PowerShell):

irm https://topaz.ooo/install.ps1 | iex

With npm, if you already have Node:

npm install -g topaz-lang

Then confirm it:

topaz version
OUTPUT
Topaz 5.2

You 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:

TOPAZ
let greeting = "Hello, Topaz!"
print(greeting)

Run it:

topaz run hello.tpz
OUTPUT
Hello, 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:

TOPAZ
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
OUTPUT
[-5, 0, 42, 1000]
[1, 4, 9]
42

Three things to notice:

  • match is an expression. classify returns its result directly. Arms take literals, ranges, bindings, and if guards.
  • "{…}" interpolation is the one way to format strings; it works on every value, including report.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:

TOPAZ
let xs = [10, 20, 30]
let i = 7
print("{xs[i]}")
topaz run fault.tpz
OUTPUT
error[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:

TOPAZ
export function shout(s: string) -> string {
    return s + "!"
}

export let greeting: string = "hello"

main.tpz, in the same directory:

TOPAZ
import util

print(util.shout(util.greeting))
topaz run main.tpz
OUTPUT
hello!

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 --run

It 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