Can a for-range loop return a value (for-range as expression)?

← Control Flow · Ref: Q80

Yes. A for-range loop can return a value by declaring a return variable inside the loop body. The return variable is updated across iterations and its final value becomes the expression result.

BASIC FOR-RANGE EXPRESSION

Accumulate a sum from 1 to 5:

  total <- for i in 1 ... 5
    <- rtn <- 0
    rtn: rtn + i

The outer '<- total' captures the loop result. The inner '<- rtn <- 0' declares the return variable initialised to 0. Each iteration adds to 'rtn'. After the loop completes, the final value of 'rtn' (15) is assigned to 'total'.

RETURN VARIABLE DECLARATION

The return variable appears right after the for-range line, indented:

  result <- for i in 1 ... limit
    <- rtn <- 0
    rtn: rtn + i

The '<- rtn <- 0' creates a variable named 'rtn' of type Integer (inferred from the literal 0), initialised to 0. You can also use typed declaration: '<- rtn as Integer: 0'.

BUILDING STRINGS

Concatenate values across iterations:

  countdown <- for i in 5 ... 1 by -1
    <- rtn <- ""
    if length rtn > 0
      rtn: rtn + ", "
    rtn: rtn + $i

Result: '5, 4, 3, 2, 1'.

WITH STEP

Expression form works with the 'by' keyword:

  sumEvens <- for i in 2 ... 10 by 2
    <- rtn <- 0
    rtn: rtn + i

Result: 30 (2+4+6+8+10).

STATEMENT VS EXPRESSION

The statement form (see Q64) executes code for side effects. The expression form computes and returns a value. Use expression form when the loop's purpose is to produce a single result.

EK9's for-range expression eliminates the need for external accumulator variables that are common in other languages. The return variable is scoped to the loop, making the intent clear.

See Q64 for for-range statement form. See Q68 for switch expression form. See Q81 for while/do-while expressions. See Q82 for for-in expression. See Q122 for stream collect as aggregation (alternative to loop accumulation). See Q237 for streams vs loops decision guide. See Q295 for single-letter loop counter conventions.

Example

defines module qa.flow.range.expression

  defines program

    ForRangeExpressionDemo()
      stdout <- Stdout()

      // === BASIC ACCUMULATION ===

      total <- for i in 1 ... 5
        <- rtn <- 0
        rtn: rtn + i
      stdout.println(`Sum 1..5: ${total}`)

      // === BUILDING A STRING ===

      countdown <- for i in 5 ... 1 by -1
        <- rtn <- ""
        if length rtn > 0
          rtn: rtn + ", "
        rtn: rtn + $i
      stdout.println(`Countdown: ${countdown}`)

      // === SUM EVEN NUMBERS ===

      sumEvens <- for i in 2 ... 10 by 2
        <- rtn <- 0
        rtn: rtn + i
      stdout.println(`Sum evens 2..10: ${sumEvens}`)

      // === FACTORIAL USING RANGE ===

      factorial <- for i in 1 ... 6
        <- rtn <- 1
        rtn: rtn * i
      stdout.println(`6! = ${factorial}`)

Common mistakes

E01070 — EK9 has no break statement. For-range expression form scopes the accumulator inside the loop and always runs to completion. Use stream pipelines if early termination is needed. See ek9 -h E01070 for details.

Incorrect:

total <- 0
      for i in 1 ... 5
        total: total + i
        if total > 10
          break

Correct:

total <- for i in 1 ... 5
        <- rtn <- 0
        rtn: rtn + i

E01072 — EK9 has no return statement. For-range expressions return the final value of the declared return variable implicitly. See ek9 -h E01072 for details.

Incorrect:

factorial <- 1
      for i in 1 ... 6
        factorial: factorial * i
      return factorial

Correct:

factorial <- for i in 1 ... 6
        <- rtn <- 1
        rtn: rtn * i
Other ways to ask this
  • How do I use a for-range loop as an expression in EK9?
  • How do I accumulate a result from a counting loop?
  • How does the for-range expression form work?

Coming from another language?

Java: no for-loop expression form, must declare accumulator outside loop. Python: list comprehension or functools.reduce() for accumulation. Rust: iterators with .fold() or .sum() or .collect(), (1..=5).sum::<i32>(). Go: no loop expression, must use external accumulator. Kotlin: (1..5).fold(0) { acc, i -> acc + i } or sumOf for numeric. C#: Enumerable.Range(1, 5).Sum() or .Aggregate() for general fold. JavaScript: Array.from({length: 5}, (_, i) => i + 1).reduce((a, b) => a + b). Swift: (1...5).reduce(0, +) for sum. EK9: for-range expression with declared return variable, accumulator scoped to loop, no external variable needed, works with Integer, Float, Duration ranges.

Keywords: flow, condition, sum, value, range, reduce, compute, control, for, accumulate, fold, expression, branch