Everything on this page is built and tested: cross-checked against the language’s
compiler/src, mz harness definition, and the example programs CI runs through mz run
with their output compared, at language main be88017, the release of 8 October 2026
(mzizi#91). It is a slice of
RFC-0013,
not all of it: the rest of the RFC is design, and what is not built
lists it. The tracker rows these features belong to are still 🟡 in the file
(what still has to be built).program <name> and its last is
end program <name>. It holds fns and, since control flow landed, enums; every statement
lives inside a fn. Exactly one fn main, with no parameters, is the entry point, and a
program’s output is what it prints. One program per file.
mz run checks a program, lowers it to a Rust package with no dependencies, builds it with
Cargo and runs it. mz build writes the same package. A program is the second thing in Mzizi
that lowers to Rust, after a service; no component lowers.
The smallest program
examples/hello.mz, verbatim:
examples/hello.mz
Functions, bindings and when
examples/fib.mz, verbatim. It recurses, binds with let and var, assigns, branches with
when and else, and interpolates.
examples/fib.mz
examples/fib.expected
fn name(a: int, b: text): int…end fn name: typed parameters, a return type or none, calls and recursion. Every path of a function with a return type returns (MZ0906); a wrong call isMZ0905, quoting the signature.letbinds a value once;varbinds one you assign with=. Scope is the block. There is no shadowing (MZ0921), a name is readable from the line after its binding to the end of its block (MZ0920), and assigning to aletisMZ0922. Python’s bare first assignment, Go’s:=and Rust’slet muteach get a fix.when/else when/else, closed byend. A condition is abooland nothing else: Mzizi has no truthiness (MZ0712).returnends a function early.print(value)writes one value’s text form and a newline; to print several, interpolate them into one text.- Comments are lines starting with
##.//and#areMZ0911, with##as theexactfix.
Values: int, float, bool and text
int and float never mix. 1 + 1.5 is MZ0912, with the exact fix 1.0 on an
int literal and the guess x.to_float() on anything else. A float prints in the shortest
form that reads back exactly: 1.0, 0.30000000000000004, 1.0e21, inf, nan.
The numeric methods are postfix, so -2.pow(2) is -(2.pow(2)):
Expressions and operators
Highest precedence first, as RFC-0013 §3.5 orders the operators that are built:
Equality is
is and is not. ==, !=, &&, || and ! are MZ0910, each with an
exact fix to the Mzizi word. += and ++ are MZ0918; str(x) and x.to_string() are
MZ0962, with the exact fix "{x}". examples/numbers.mz prints its arithmetic, and
numbers.expected
holds what it prints.
Control flow
Fromexamples/control.mz (an excerpt; the file is in the repository, and CI compares its
output with examples/control.expected):
examples/control.mz (excerpt)
match <value>takescaselines, each listing one or more values (case 6 7), then an optionalelse, over an enum, anint, atextor abool. It is checked for exhaustiveness: a missing case isMZ0930, naming the missing variants, and a case that can never run isMZ0931, whoseexactfix deletes it. Amatchover afloatisMZ0711: compare a float withwhen.for each i in range(a, to = b)counts fromaup to but not includingb.rangeis read only on afor eachline;for eachover a list waits for lists.while <condition>is the one conditional loop;while truewith abreakis the loop that ends from inside.breakandcontinueact on the innermost loop, and areMZ0935outside one.whenandmatchas values. Either may be the whole value of alet, avar, an assignment or areturn, each branch one expression line; nowhere else, and there is no ternary (MZ0932).- Other languages’ spellings are fixed.
elif,else if,switch,default:,case _and_ =>areMZ0933;for x in xs,for (const x of xs),loopandrange(n)areMZ0934, each with anexactfix. Anelse whenchain over one enum’s variants isMZ0936, with aguessfix rewriting it as amatch.
enum, one variant per line, closed by a bare end. A
variant is written bare where the expected type settles its enum, and <enum>.<variant>
where two enums share it. Variants compare with is, order by declaration and print as their
names. A variant may carry columns, a name and a literal each, read with a dot
(problem.say), as in examples/errors.mz below.
Errors: result, error and try
examples/errors.mz, verbatim:
examples/errors.mz
examples/errors.expected
- A function that can fail returns
result(T, E), orresult(none, E).return vreturns success,return error(e)returns failure, andreturn rpasses a result of the function’s own type through. - A caller matches the result, with exactly two cases,
case ok <name>andcase error <name>, or propagates its error with a prefixtry, in a function that returns a result with the same error type. - Any other use of a result is
MZ0950: discarding it, printing it, comparing it, passing it or holding it in avar. A result is never a parameter, nor inside another result. fn mainmay returnresult(none, E). When it returns an error, the program writesmz: error MZ0992: main returned an error: <the error's text form>to standard error and exits 1.- Other languages’ idioms are
MZ0952:Ok(v)(exactv),Err(e)(exacterror(e)), Rust’s postfix?(exactprefixtry),throw eandraise e(return error(e)), and.unwrap()and.expect(…), which have no fix: an error is never turned into a crash.
mz run
- Check. On any error it prints the diagnostics, exits 3, and does not build.
- Lower. It writes a Cargo package with no dependencies into
<name>-<path hash>under$MZ_CACHE_DIR, ormzizi/mz-run/under$XDG_CACHE_HOMEor~/.cache. With none of the three set it exits 2 and names them; it never falls back to the system’s temporary directory. - Build.
cargo build --offline --quiet, with--releasewhen asked. No dependencies means no network. - Run. The binary runs with the terminal’s standard input, output and error. Standard
output belongs to the program; everything
mz runwrites of its own goes to standard error.
mz run departs from the other commands, where 1 means
errors, because 1 belongs to the program it runs:
RFC-0013 §13 also designs exit 70 and
MZ0993 for a runtime failure, and mz run --agent
for NDJSON on standard error. Neither is built: mz run --agent exits 2.
The lowering. Every value is owned and no reference or lifetime is emitted: a text read
is a .clone(). result(T, E) is Rust’s Result<T, E> and try e is e?; an enum is a
Rust enum; a match on an enum or a bool has no _ arm unless it has an else, so
rustc checks exhaustiveness again; for each i in range(a, to = b) is for i in a..b. The
generated code holds no unwrap, expect, panic! or unsafe, which a test checks.
Nesting is capped
A program’s blocks and expressions nest at most 32 deep together, and an expression’s tree of binary operators at most 64. Past the cap is oneMZ0411 per file, not a stack overflow.
compiler/tests/robustness.rs holds every case to a 1 MiB stack.
What is not built
These are designed in RFC-0013 and are not in the compiler. Each isMZ0919, which says the
form is designed and not built, or another code that names it:
- Lists, maps and sets in a program, indexing, and
for eachover a list (C7). - Text methods (C6), records and methods (C8), options in function bodies, and a decimal type.
- Named arguments beyond
range’sto,try … via f(MZ0955), and amatchstub as the fix forMZ0950. - A program’s
contractblock,mz test,mz run --agent, andmz outline,mz irandmz hashon a program, which exit 2. - Modules: one program per file, and nothing is imported.
MZ09xx code.