Control flow#
> Two branch forms, three loop forms, and a bounded loop you will not find in other languages.
if / else if / else#
Conditions take no parentheses. Braces are mandatory even for one statement.
-- `if` / `else if` / `else`. Conditions take no parentheses.
task describe(power: int) -> word {
if power > 9000 {
give back "over nine thousand"
} else if power > 5000 {
give back "high"
} else if power > 1000 {
give back "moderate"
} else {
give back "low"
}
}
task main() -> void {
say describe(9001)
say describe(6000)
say describe(2000)
say describe(10)
}
over nine thousand
high
moderate
low
else if chains to any depth. Internally V3 flattens these rather than nesting them, which is one of the architectural fixes that made the self-hosted compiler possible.
when#
when matches a subject against literal arms. _ is the catch-all.
-- `when` matches a subject against literal arms. `_` is the catch-all.
-- Each arm takes one statement; use a block to run several.
task main() -> void {
pilot code: int = 2
when code {
1 -> say "launch"
2 -> {
say "hold"
say "await orders"
}
3 -> say "abort"
_ -> say "unknown"
}
pilot phase: word = "sortie"
when phase {
"briefing" -> say "in the briefing room"
"sortie" -> say "in the air"
_ -> say "elsewhere"
}
}
hold
await orders
in the air
Rules:
- Each arm is
pattern -> statement. One statement — wrap several in a block. - Arms are separated by nothing at all; newlines are enough.
- The subject may be
int,word,boolornum. - Arm patterns are expressions compared for equality, not patterns. There is no destructuring, no binding, no guards, no ranges.
- Exhaustiveness is not checked. Without a
_arm and with no match, nothing runs. whenis a statement, not an expression. It cannot appear on the right of=or insidegive back.
To produce a value from a match, assign inside the arms:
task main() -> void {
pilot code: int = 2
pilot mut label: word = ""
when code {
1 -> label = "launch"
2 -> label = "hold"
_ -> label = "unknown"
}
say label
}Destructuring arms — BETA::Soldier { position } -> engage_at(position) — need variants, which V3 does not have. So does check over maybe / result, and check route. See Not in V3.
Loops#
V3 has exactly three loop forms.
-- V3 has three loop forms: a counted loop, a condition loop,
-- and the bounded `training arc`.
task main() -> void {
-- repeat N times
pilot mut total: int = 0
repeat 5 times {
total += 2
}
say word_from_int(total)
-- repeat until CONDITION (tested before each pass)
pilot mut countdown: int = 3
repeat until countdown == 0 {
say word_from_int(countdown)
countdown -= 1
}
-- break and continue work in every loop form
pilot mut seen: int = 0
repeat 10 times {
seen += 1
if seen == 3 { continue }
if seen == 6 { break }
}
say word_from_int(seen)
}
10
3
2
1
6
repeat N times#
repeat COUNT times { body }COUNT is any int expression, evaluated once before the loop. There is no loop variable — keep your own counter if you need the index.
pilot mut i: int = 0
repeat array_len(items) times {
say array_get(items, i)
i += 1
}repeat until#
repeat until CONDITION { body }The condition is tested before each pass, so the body may run zero times. Note the sense: the loop continues while the condition is false.
training arc#
training arc until CONDITION max N sessions { body }A loop with a compulsory iteration cap. It stops when the condition becomes true or when the cap is reached — whichever comes first — so it cannot spin forever.
-- `training arc` is a loop with a mandatory session cap.
-- It stops when the condition becomes true OR when the cap is hit,
-- so it cannot spin forever.
task main() -> void {
pilot mut power: int = 0
training arc until power >= 100 max 8 sessions {
power += 20
}
say word_from_int(power)
-- The cap wins when the condition is never reachable.
pilot mut stuck: int = 0
training arc until stuck > 1000 max 4 sessions {
stuck += 1
}
say word_from_int(stuck)
}
100
4
It lowers to roughly:
int64_t sessions = 0;
while (!(CONDITION) && sessions < N) {
body;
sessions++;
}This makes it genuinely useful for bounded numeric search, where you want a hard guarantee of termination:
-- A slightly larger program: integer square root by bisection, driven by
-- a `training arc` so the loop is provably bounded.
shape Bounds {
lo: int
hi: int
}
task isqrt(target: int) -> int {
if target < 2 { give back target }
pilot mut b: Bounds = Bounds { lo: 1, hi: target }
training arc until b.lo >= b.hi max 64 sessions {
pilot mid: int = (b.lo + b.hi + 1) / 2
if mid * mid > target {
b.hi = mid - 1
} else {
b.lo = mid
}
}
give back b.lo
}
task main() -> void {
say word_from_int(isqrt(0))
say word_from_int(isqrt(1))
say word_from_int(isqrt(15))
say word_from_int(isqrt(16))
say word_from_int(isqrt(17))
say word_from_int(isqrt(1000000))
}
0
1
3
4
4
1000
The with growth variant — which makes the compiler verify that the body actually mutates the condition's subject — is V4. Plain training arc is fully supported.
break and continue#
Both work in all three loop forms.
repeat 10 times {
if should_skip { continue }
if should_stop { break }
process()
}Bare blocks#
A { ... } on its own is a statement. It groups code but does not create a new scope for bindings — a pilot declared inside remains visible afterwards.
task main() -> void {
pilot x: int = 1
{
say word_from_int(x)
}
}eventually#
eventually { ... } is the cleanup block. Its V3 behaviour differs sharply from the specification.
-- `eventually { ... }` marks a cleanup block.
--
-- IMPORTANT V3 caveat: V3 emits the block INLINE at the point it is
-- written. It is not deferred to the end of scope and it does not run
-- on `give back`, `break` or `panic`. Place it where you want it to run.
task main() -> void {
say "engaging"
eventually {
say "cleanup"
}
say "disengaged"
}
engaging
cleanup
disengaged
V3 emits the block inline, where you wrote it. It is not deferred to the end of scope, it does not run in LIFO order with other eventually blocks, and it does not run on give back, break or panic. Treat it as a labelled section, not as defer. True deferred semantics are V4.
No for each#
for each lexes as a keyword but has no grammar in V3:
error: unexpected 'for each' — this token cannot start an expressionIterate with an index instead:
pilot mut i: int = 0
repeat array_len(items) times {
say array_get(items, i)
i += 1
}Summary of what is missing#
| Construct | Status |
|---|---|
for each x in list | V4 |
for each (i, x) in list.enumerate() | V4 |
check over maybe / result | V4 |
check route | V4 |
when with destructuring or guards | V4 |
when in expression position | V4 |
training arc ... with growth | V4 |
xm3 { a || b } and all squadron concurrency | V4 |
prob[0.3] chance { }, prob_when | V4 |
isekai { } bringing back { } | V4 |
eventually if cond { } | V4 |
True deferred eventually | V4 |