Zith's control flow combines familiar C-like constructs with pattern matching, chain flow, and flow functions.
Parentheses () are mandatory on all control structure conditions except function calls. Logical operators use English keywords; bitwise use standard symbols followed by ..
if (x > 0 and y < 10) { ... }
if isTrue() and (x > 5) { ... }
let mask = a &. b |. c ^. d;
if / elseif (x > 0) {
@println("positive");
} else (x < 0) {
@println("negative");
} else {
@println("zero");
}
// As expression
let sign = if (x > 0) { "pos" } else { "neg" };
for — Loop Constructsfor { ... } // infinite
for (i in 0..=9) { @println(i); } // inclusive range
for (i in 0..9) { @println(i); } // exclusive range
for (i = 0), (i < 10), (i += 1) { ... } // init / cond / step
for (v in range(0, 100)) { @println(v); } // over a generator
// Destructure group with fallback
let r = for ([acc, i]: i32), (i in 0..n) { acc *= i + 1 } or 0;
ℹ Note: If the loop may not run, the return is deduced as optional — unless or collapses it to non-optional.
when — Pattern Matchingwhen (count) {
0 = @println("none"),
1 = @println("one"),
2..10 = @println("few"),
_ = @println("many"),
}
// As expression
let label = when (score) { 90..100 = "A", 70..90 = "B", _ = "C" };
// With branch tags
when (v) {
n: i32 = @println("int: {n}"),
f64 = @println("float: {v}"),
_ = @println("other"),
}
->)The -> operator pipes output left-to-right. The previous value is ... Tags capture values for later use.
getData() -> process(..) -> save(..);
getData()
-> raw: parse(..)
-> parsed: validate(..)! // ! propagates out
-> connectDb()
-> save(parsed);
// Inline block
readFile("data.bin")
-> { let h = parse_header(..); validate(h)! }
-> process_body(..);
flow fn & MarkersFlow functions enable cooperative multitasking through marker, dock, and jump.
flow fn run(data: Stream): void {
marker Process(chunk: Chunk, count: i32) {
transform(chunk);
}
let i = 0;
for item in data {
dock { jump Process(item, i); }
i += 1;
}
}
// Global marker
marker ContextSwitch(next: TaskId) {
saveRegisters();
loadTask(next);
}
// noreturn: dock return variable never modified
flow fn scheduler(): noreturn { ... }
thread_local blob; bits preserved across invocations unless explicitly updated.noreturn.By default, markers are stackless. The stackful modifier opts into stackful behaviour: when a jump is executed from inside a stackful marker, all local owned nodes are dropped before the jump.
flow fn run(data: Stream): void {
stackful marker Process(chunk: Chunk) {
let buffer = allocate(chunk.size); // owned — dropped before jump
transform(buffer);
}
}
See also: Spec: Control Flow
Next: Memory Model — NRA