Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Chapter 8. Bounded Repetition

Goal

By the end of this chapter you will be able to write a loop, and you will understand the one rule that makes Keleusma loops different from loops in most other languages.

A loop with a known count

A repeat sign in sheet music says how many bars to repeat. It never says “repeat for a while, and we shall see.” Keleusma loops are the same. Every loop has a count that is known before the loop begins. This is what the word bounded means in the chapter title.

A loop is written with for. It walks through a range of numbers, or through the entries of an array:

fn main() -> Word {
    let durations = [4, 4, 8, 2];

    for d in durations {
        let _step = d * 2;
    }

    for beat in 0..4 {
        let _tick = beat;
    }

    durations[2]
}

Run it with keleusma run. The output is:

8

The first loop walks through the four entries of the array durations, binding each in turn to d. The second loop walks through the range 0..4, which is the four numbers 0, 1, 2, and 3. Both loop counts are fixed before the loop starts: an array has a known length, and a range states its bounds.

The honest limitation

Look closely at that program. The two loops run, but the result, 8, does not depend on them. It is durations[2], the entry at position 2 of the array, computed without any loop at all.

This is deliberate, and it follows directly from Chapter 5. A binding cannot be reassigned. A loop therefore cannot keep a running total, because a running total is a value that changes on every pass. Inside an atomic function, the kind declared with fn, a loop can walk through values but cannot accumulate a result from them.

So why learn the loop now? Because the loop earns its place in Part IV, inside a different kind of function called a loop function. There, each pass of the loop can hand a value to the host or update stored state, and the repetition does real work. This chapter teaches the shape of the loop so that it is already familiar when it matters.

Leaving a loop early

A loop can stop before its count is reached, with break:

for beat in 0..16 {
    if beat == 4 {
        break;
    }
}

When beat reaches 4, break ends the loop at once. Note the semicolon after break. Because break can stop the loop early, the count is still bounded: the loop runs at most its full count, and possibly fewer passes, but never more.

What you now know

  • for name in 0..n { ... } loops over a range of numbers.
  • for name in array { ... } loops over the entries of an array.
  • Every loop count is known before the loop begins.
  • Inside an atomic fn, a loop cannot accumulate a result, because bindings do not change. The loop does real work in Part IV.
  • break; leaves a loop early.

The next chapter threads a value through a chain of functions.