Project

Moving to 5.21

Change the language mode, replace every form removed in 5.21 with its method or std module call, and check the result. Source from before v5 is covered at the end.

Topaz 5.21 removes global functions such as print, static namespaces such as Math and FS, and six standard modules. Their calls move to value methods and std modules. Switch the language mode first, then let topaz check point to each removed form.

Install Topaz and keep a copy of the original source, inputs, and outputs.

Change the language mode

In topaz.toml, set language and std to "5.21".

TOML
[package]
language = "5.21"

[dependencies]
std = "5.21"

Topaz 5.21 accepts only the topaz-5.21 language mode. A package left on the 5.20 language mode stops with unsupported language mode. A file checked on its own, without topaz.toml, already uses 5.21.

Find each removed form

This file still calls print, which 5.21 removed.

print("ready")

Save it as old-forms.tpz and check it.

BASH
topaz check old-forms.tpz

For a removed call such as print here, the checker reports TPZ5002 and names the call to write in 5.21. For an import of a removed standard module such as import std.math, it reports TPZ3001 and names the methods to use instead.

Output
error[TPZ5002]: `print` is not bound; import std.io and use io.writeLine(value) in 5.21
 --> old-forms.tpz:1:1
  |
1 | print("ready")
  | ^^^^^

old-forms.tpz: 1 type diagnostic

Follow the message and save the file again.

TOPAZ
import std.io

io.writeLine("ready")

Now topaz check old-forms.tpz succeeds, and topaz run old-forms.tpz prints ready.

Replace removed forms

Replace each form in the first column with the form in the second. When the third column names an import, add it once at the top of the file.

Removed in 5.21Write insteadImport first
print(x)io.writeLine(x)import std.io
input()io.readStdin()import std.io
open(p)io.open(p)import std.io
assert(c)test.assert(c)import std.test
map(xs, f), filter(xs, f), reduce(xs, z, f)xs.map(f), xs.filter(f), xs.reduce(z, f)
toFloat(n), toInt(s), toIntRadix(s, r), fromCodePoint(n)n.toFloat(), s.toInt(), s.toIntRadix(r), n.toCodePoint()
Math.sqrt(x), Math.abs(x), Math.sin(x), Math.cos(x), Math.tan(x), Math.isNaN(x), Math.isFinite(x)x.sqrt(), x.abs(), x.sin(), x.cos(), x.tan(), x.isNaN(), x.isFinite()
Math.floor(x), Math.ceil(x), Math.round(x)x.floor(), x.ceil(), x.round()
Math.parseFloat(s)s.parseFloat()
Math.min(a, b), Math.max(a, b)a.atMost(b), a.atLeast(b)
JSON.parse(s), JSON.stringify(v), JSON.parseAs<T>(s)json.parse(s), json.stringify(v), json.parseAs<T>(s)import std.json
JSON.decode<T>(v)v.decode<T>()
FS.readText(p), FS.writeText(p, text), FS.readBytes(p), FS.writeBytes(p, bytes), FS.list(p)fs.readText(p), fs.writeText(p, text), fs.readBytes(p), fs.writeBytes(p, bytes), fs.list(p)import std.fs
CSV.parse(s), CSV.parseWithHeader(s), CSV.stringify(rows), CSV.stringifyWithHeader(rows, columns)csv.parse(s), csv.parseWithHeader(s), csv.stringify(rows), csv.stringifyWithHeader(rows, columns)import std.csv
TOML.parse(s), TOML.stringify(v), TOML.fromJson(v)toml.parse(s), toml.stringify(v), toml.fromJson(v)import std.toml
TOML.toJson(t)t.toJson()
Test.assert(c), Test.assertEq(a, b), Test.assertNe(a, b), Test.assertContains(text, part)test.assert(c), test.assertEq(a, b), test.assertNe(a, b), test.assertContains(text, part)import std.test
Test.assertOk(result), Test.assertErr(result), Test.assertSome(option), Test.assertNone(option), Test.assertGolden(name, actual)test.assertOk(result), test.assertErr(result), test.assertSome(option), test.assertNone(option), test.assertGolden(name, actual)import std.test
Encoding.utf8Encode(s), Encoding.utf8Decode(b)s.encodeUtf8(), b.decodeUtf8()
Encoding.hexEncode(b), Encoding.hexDecode(s)b.toHex(), Bytes.fromHex(s)
Encoding.base64Encode(b), Encoding.base64Decode(s)b.toBase64(), Bytes.fromBase64(s)
Hash.sha256(b), Hash.sha512(b), Hash.crc32(b)b.sha256(), b.sha512(), b.crc32()
Hash.hmacSha256(key, data)key.hmacSha256(data)
Codec.gzipCompress(b), Codec.gzipDecompress(b), Codec.deflateCompress(b), Codec.deflateDecompress(b)b.gzipCompress(), b.gzipDecompress(), b.deflateCompress(), b.deflateDecompress()
Codec.deflateFixedCompress(b), Codec.zlibFixedCompress(b), Codec.reedSolomon255223Protect(b)b.deflateFixedCompress(), b.zlibFixedCompress(), b.reedSolomon255223Protect()
Codec.zstdCompress(b, level), Codec.zstdDecompress(b)b.zstdCompress(level), b.zstdDecompress()
Cli.hasFlag(args, name), Cli.option(args, name), Cli.options(args, name), Cli.positionals(args)args.hasFlag(name), args.option(name), args.options(name), args.positionals()
Array.of(a, b)[a, b]
Set.of(a, b)set { a, b }
Map.new()map {}
Bytes.encodeUtf8(s), Bytes.concat(a, b)s.encodeUtf8(), a.concat(b)
BigInt.fromInt(n), Decimal.fromInt(n)n.toBigInt(), n.toDecimal()
import std.math, import std.encoding, import std.hash, import std.codec, import std.cliDelete the import and call the methods in the rows above
import std.gen[for x in lo..hi => x], [false, true]
topaz lispex runlispex run for Lispex source, topaz lispex embed run for the embedded evaluator
topaz lispex infotopaz lispex embed info

x.floor(), x.ceil(), x.round(), and x.trunc() return int. When you need the float that Math.floor(x) gave, write x.floor().toFloat(), and do the same for ceil and round. The new integer conversion checks its result range.

std.bytes, std.path, std.regex, std.url, std.date, std.bigint, and std.decimal export only types. Call their functions as methods of the value or as type functions such as Bytes.fromHex.

User functions and imported namespaces keep their declared meaning. When a call uses named arguments, store the argument expressions in temporary values in their original evaluation order before you move the receiver. Replace a builtin function used as a value with an explicitly typed lambda.

Move source written before v5

Rewrite Topaz source written before v5 by hand. Preserve the original behavior and compare the results after each change. Compatible product updates need no separate source migration.

Rewrite one file

This historical example uses old declaration and array-type syntax.

mut let names: [string] = ["Ada"]
let first = names[0]

Save the current form below as from-pre-v5.tpz in a working folder.

TOPAZ
import std.io

let mut names: Array<string> = ["Ada"]
let first: Option<string> = names.get(0)
io.writeLine("{first}")

Run these commands from that folder.

BASH
topaz check from-pre-v5.tpz
topaz run from-pre-v5.tpz

The check exits with code 0. The run exits with code 0 and prints this result.

Output
Some(Ada)

names.get(0) returns an Option<string>. The first element exists, so the result is Some(Ada). An index outside the array returns None. Direct array indexing remains supported and raises a runtime fault for an out-of-range index. Choose the behavior your program needs.

When documenting old code, use a text block. Use a topaz block for current syntax.

Record behavior before editing a project

Record the entry files, module roots, inputs, expected outputs, runtime faults, and file or network effects. Keep captured results for comparison. Identify examples written in Rust, JavaScript, or framework syntax separately from Topaz source.

Adapt source and package metadata to the current language mode. Use Packages and Dependencies for package settings. Consult Classic for historical syntax and the current manual for the form to write now.

Replace recurring forms

Old formCurrent formWhat to preserve
mut let xlet mut xWhether the binding can be reassigned
[T]Array<T>Element type
function(T) -> U(T) -> UParameter and return types
[head, ...tail][head, ..tail]Remaining elements in a list pattern
args: ...T...args: TVariadic argument type
Anonymous function(x: T) => exprParameters, result, and captured bindings
s[i], s.lengths.scalars() and explicit string APIsUnicode scalar positions and counts
Backticks, ${expr}Double quotes, {expr}String contents and interpolation

A Unicode scalar is one Unicode value, which can differ from a byte or a visible character. See Strings & Templates for indexing and registered template tags. Use self where the current method declaration permits it.

Use Result for recoverable failures and Option or a nullable type for missing values. Keep runtime faults distinct from returned errors. Move cleanup to defer or supported using statements. Update import, export, visibility, and initialization according to Modules & Visibility.

Keep foreign-language code in explicitly labeled build or conversion steps. It cannot be pasted into a Topaz module as Topaz syntax.

Compare the rewritten program

Run topaz check on the rewritten source, then run the project's tests. For the file above, compare the output with Some(Ada). For a project, compare the recorded inputs and results.

Check module initialization, iteration order, Unicode scalar positions, numeric faults, cleanup, and permitted host operations. If a diagnostic appears, fix its source location using the linked current syntax. If output differs after the check passes, inspect the changed logic with the same input.

Build for the native, Web, or Worker environments you deploy to, following Build Output. Unsupported forms must produce a diagnostic before product output. Finish when checks and tests pass and each selected build produces the expected behavior.