FREAKV3 docs freak 0.14.2 (Maverick)

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.

examples/control_if.fk compilesruns
-- `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)
}
Program output
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.

examples/control_when.fk compilesruns
-- `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"
    }
}
Program output
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, bool or num.
  • 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.
  • when is a statement, not an expression. It cannot appear on the right of = or inside give back.

To produce a value from a match, assign inside the arms:

FREAK
task main() -> void {
    pilot code: int = 2
    pilot mut label: word = ""
    when code {
        1 -> label = "launch"
        2 -> label = "hold"
        _ -> label = "unknown"
    }
    say label
}
Not in V3

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.

examples/control_loops.fk compilesruns
-- 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)
}
Program output
10
3
2
1
6

repeat N times#

FREAK
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.

FREAK
pilot mut i: int = 0
repeat array_len(items) times {
    say array_get(items, i)
    i += 1
}

repeat until#

FREAK
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#

FREAK
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.

examples/training_arc.fk compilesruns
-- `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)
}
Program output
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:

examples/bounded_search.fk compilesruns
-- 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))
}
Program output
0
1
3
4
4
1000
Not in V3

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.

FREAK
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.

FREAK
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.

examples/eventually.fk compilesruns
-- `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"
}
Program output
engaging
cleanup
disengaged
Careful

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 expression

Iterate with an index instead:

FREAK
pilot mut i: int = 0
repeat array_len(items) times {
    say array_get(items, i)
    i += 1
}

Summary of what is missing#

ConstructStatus
for each x in listV4
for each (i, x) in list.enumerate()V4
check over maybe / resultV4
check routeV4
when with destructuring or guardsV4
when in expression positionV4
training arc ... with growthV4
xm3 { a || b } and all squadron concurrencyV4
prob[0.3] chance { }, prob_whenV4
isekai { } bringing back { }V4
eventually if cond { }V4
True deferred eventuallyV4

Every FREAK snippet on this site was compiled by freak 0.14.2 (Maverick), built from source with a verified self-host fixed point. 45/45 examples compile; regenerate with python tools/verify.py then python tools/build_docs.py.