Heroes / Guide / chapter 6

Control flow

Choose with if; repeat with for or while.

Control flow is the order in which statements run. Heroes provides if to choose which statements to run, for to iterate over a sequence, and while to repeat a block while a condition is true.

if, and only bool

A condition must have type bool, with a value of true or false. Numbers and strings are not automatically converted to conditions:

$ heroes check loops.hero
error[not_bool]: an `if` condition must be `bool`, found `i64` — there is no truthiness in this language
  at loops.hero:13:12
     |
  13 |         if x
     |            ^

Use if, else if and else to define branches. The condition needs no parentheses; indentation defines the body. if is also an expression, meaning it can produce a value. You can use it to choose a value without assigning separately in two branches.

for x in xs: walking a sequence

# The largest element. Fails on nothing: an empty array has no largest, so
# the caller is asked for a floor instead.
function largest_or(xs: [i64], floor: i64) -> i64
    best: i64 @= floor

    for x in xs
        if x > best
            best @ x

    return best
examples/gallery/04-loops.hero, lines 7–16

The loop visits one element at a time. On each iteration, the variable holds a fresh copy of that element. There is no index or counter to update.

The next chapter explains the @= and @ symbols. Here best: i64 @= floor declares a mutable variable, and best @ x assigns a new value to it. A name bound with = cannot be reassigned.

Leaving early: break

# Sums until the running total would pass a limit, then stops.
function sum_until(xs: [i64], limit: i64) -> i64
    total: i64 @= 0

    for x in xs
        if total + x > limit
            break
        total @ total + x

    return total
examples/gallery/04-loops.hero, lines 18–27

Skipping one: continue

# Counts the even ones, and shows `continue` earning its keep.
function even_count(xs: [i64]) -> i64
    seen: i64 @= 0

    for x in xs
        if x % 2 != 0
            continue
        seen @ seen + 1

    return seen
examples/gallery/04-loops.hero, lines 29–38

break ends the innermost loop; continue skips to its next iteration. You cannot name an outer loop with a label. To leave several loops, you can put them in a function and use return to exit it.

while: repeating on a condition

# The conditional loop, with the counter visible: `while cond` has no header
# that hides where the index changes.
function digits_in(n: i64) -> i64
    left: i64 @= n
    digits: i64 @= 1

    while left >= 10
        left @ left / 10
        digits @ digits + 1

    return digits
examples/gallery/04-loops.hero, lines 40–50

A while checks its condition before each iteration. If a counter needs updating, write that update in the body in the order it should run.

Counting: a range is a function call

# The loop variable is not needed, so it is `_` — the same wildcard a pattern
# uses, and the reason an unused name is never an accident here.
function repeat_dash(times: i64) -> str
    out: str @= ""

    for _ in range(from: 0, to: times)
        out @ out + "-"

    return out
examples/gallery/04-loops.hero, lines 52–60

range returns a list of numbers, including from and excluding to. You must name both arguments because they share the type i64, as explained in chapter 3.

Use _ as the loop variable when you do not need the element’s value. As in a match, _ discards the value without binding a name, so it cannot produce an unused-name error.

All of it, running

function main()
    print(repeat_dash(20))
    print("digits in 2026: ", digits_in(2026))
examples/gallery/04-loops.hero, lines 70–72
$ heroes run examples/gallery/04-loops.hero
--------------------
digits in 2026: 4

Appending text to a string in a loop copies the entire string each time, so each iteration costs more. For long strings, use repeat or join, which build the result in one pass. The strings chapter explains the difference.

Next: Bindings and mutation, learn to use = and @ and avoid ambiguous names. Back to the chapter list. ← Previous: Failure is a value

Run a program of your own: it takes four commands. Or take the whole language in one file, and every program already written in it.