Which control flow constructs can be used as expressions in EK9?
← Control Flow · Ref: Q83
EK9 allows six control flow constructs to be used as expressions that return values: switch, for-range, for-in, while, do-while, and try. All follow the same pattern: declare a return variable with '<-' inside the body.
THE UNIVERSAL PATTERN
Every expression form uses the same structure:
result <- CONTROL_FLOW_CONSTRUCT <- rtn <- initialValue ... body updates rtn ...
The outer '<- result' captures the expression value. The inner '<- rtn <- initialValue' declares and initialises the return variable. The body modifies 'rtn' across iterations or branches. The final value of 'rtn' becomes the result.
SWITCH EXPRESSION (see Q68)
label <- switch priority <- rtn as String? case 1 rtn: "HIGH" default rtn: "NORMAL"
FOR-RANGE EXPRESSION (see Q80)
total <- for i in 1 ... 10 <- rtn <- 0 rtn: rtn + i
FOR-IN EXPRESSION (see Q82)
joined <- for item in items <- rtn <- "" rtn: rtn + item
WHILE EXPRESSION (see Q81)
result <- while counter <- 0 with counter < limit <- rtn <- 0 rtn: rtn + counter counter: counter + 1
DO-WHILE EXPRESSION (see Q81)
result <- do counter <- 0 <- rtn <- 0 rtn: rtn + counter counter: counter + 1 while counter < limit
STATEMENT VS EXPRESSION FORM
Statement form: performs side effects (I/O, mutation). No return variable.
for i in 1 ... 10 stdout.println($i)
Expression form: computes and returns a value. Has return variable.
total <- for i in 1 ... 10 <- rtn <- 0 rtn: rtn + i
IF IS NOT AN EXPRESSION
Unlike the six constructs above, if/else in EK9 is always a statement. For a value chosen by a condition, use the TERNARY operator — 'grade <- score >= passMark <- "A" : "B"' (or 'else' in place of ':'), which is the if-expression equivalent (see Q1360) — or a switch expression for multi-way selection.
WHY EXPRESSIONS MATTER
Expression forms eliminate temporary accumulator variables that pollute the outer scope. The return variable is scoped to the construct, making intent clear and preventing accidental reuse.
See Q68 for switch expression. See Q80 for for-range expression. See Q81 for while/do-while expression. See Q82 for for-in expression.
See Q80 for for-range expressions. See Q81 for while expressions. See Q82 for for-in expressions.
Example
defines module qa.flow.expression.overview defines function supplyPriority() as pure -> level as Integer <- rtn as Integer: level supplyLimit() as pure -> initial as Integer <- rtn as Integer: initial defines program ExpressionOverviewDemo() stdout <- Stdout() // === SWITCH EXPRESSION === priority <- supplyPriority(2) label <- switch priority <- rtn as String? case 1 rtn: "HIGH" case 2 rtn: "MEDIUM" default rtn: "LOW" stdout.println(`Priority: ${label}`) // === FOR-RANGE EXPRESSION === total <- for i in 1 ... 5 <- rtn <- 0 rtn: rtn + i stdout.println(`Sum 1..5: ${total}`) // === FOR-IN EXPRESSION === words <- ["EK9", " ", "rocks"] joined <- for word in words <- rtn <- "" rtn: rtn + word stdout.println(`Joined: ${joined}`) // === WHILE EXPRESSION === limit <- supplyLimit(4) whileResult <- while counter <- 0 with counter < limit <- rtn <- 0 rtn: rtn + counter counter: counter + 1 stdout.println(`While sum 0..3: ${whileResult}`) // === DO-WHILE EXPRESSION === doResult <- do counter <- 0 <- rtn <- 0 rtn: rtn + counter counter: counter + 1 while counter < limit stdout.println(`Do-while sum 0..3: ${doResult}`)
Common mistakes
E01072 — EK9 has no return statement. All five expression forms (switch, for-range, for-in, while, do-while) use declared return variables. The value is returned implicitly. See ek9 -h E01072 for details.
Incorrect:
switch priority case 1 return "HIGH" case 2 return "MEDIUM" default return "LOW"
Correct:
label <- switch priority <- rtn as String? case 1 rtn: "HIGH" case 2 rtn: "MEDIUM" default rtn: "LOW"
E01070 — EK9 has no break statement. For-range expressions scope the accumulator inside the loop and always run to completion. 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
Other ways to ask this
- What loops can return values in EK9?
- How do expression forms work across different control flow constructs?
- What is the expression vs statement distinction in EK9 control flow?
Coming from another language?
Java: switch expression (Java 14+), no loop expressions, Stream.reduce() for functional fold. Rust: all blocks are expressions, match/if/loop can all return values, for and while cannot. Go: no expression forms for any control flow. Kotlin: when is expression, no loop expressions, fold/reduce for functional patterns. Python: ternary expression and comprehensions, no loop expressions. C#: switch expression (C# 8+), no loop expressions, LINQ Aggregate for fold. JavaScript: ternary only, no loop or switch expressions. Swift: no loop expressions, switch not an expression. EK9: switch, for-range, for-in, while, and do-while all have expression forms with uniform return variable pattern.
Keywords: pattern, branch, control, condition, flow, form, return, switch, constant, overview, expression, value, loop, statement