How do I implement retry logic without break?
← Control Flow Without break/continue/return · Ref: Q283
Every language uses while(true) { try... break } for retries. EK9 has no break AND no while(true). Use a counter-bounded for-range loop with a guard on success.
THE PROBLEM
In Java you write: while (true) { result = tryOp(); if (result != null) break; retries++; if (retries >= max) break; }. This has two break conditions, an infinite loop, and requires careful counting.
EK9 PATTERN: FOR-RANGE WITH GUARD
Use a for-range loop bounded by max attempts and guard on the result:
retryOperation()
-> maxRetries as Integer
<- result as String: String()
for attempt in 1 ... maxRetries
if ~result?
result :=? tryOnce(attempt)
The for-range is bounded by design (no infinite loop). The guard on ~result? skips further attempts once a result is set. The :=? ensures only the first successful result is kept.
WHY BETTER THAN BREAK
The loop is bounded by construction. There is no infinite loop risk. The intent is clear: try up to N times, stop on first success. The ~result? check at the top of each iteration replaces the break-on-success pattern.
SIMPLIFIED PATTERN
For simpler cases where the operation returns a set or unset value:
for attempt in 1 ... maxRetries if ~result? outcome <- tryOnce(attempt) result :=? outcome
The :=? assigns only if result is still unset, so the first success wins.
See Q134 for try-catch patterns. See Q144 for why break was removed. See Q64 for for-range loops.
Example
defines module qa.without.retry defines function tryOnce() as pure -> attempt as Integer <- outcome as String: String() successAttempt <- 3 if attempt == successAttempt outcome: "success on attempt 3" tryAlwaysFails() as pure -> attempt as Integer <- outcome as String: String() require attempt? retryOperation() -> maxRetries as Integer <- result as String: String() for attempt in 1 ... maxRetries if ~result? outcome <- tryOnce(attempt) result :=? outcome retryExhausted() -> maxRetries as Integer <- result as String: String() for attempt in 1 ... maxRetries if ~result? outcome <- tryAlwaysFails(attempt) result :=? outcome defines program RetryDemo() stdout <- Stdout() // === RETRY WITH SUCCESS ON 3RD ATTEMPT === successResult <- retryOperation(5) if successResult? stdout.println(`Retry succeeded: ${successResult}`) else stdout.println("Retry failed") // === RETRY WITH ALL ATTEMPTS FAILING === failResult <- retryExhausted(5) if failResult? stdout.println(`Unexpected success: ${failResult}`) else stdout.println("All 5 attempts failed as expected") // === INLINE RETRY PATTERN === inlineResult <- String() for attempt in 1 ... 4 if ~inlineResult? minSuccessAttempt <- 2 if attempt >= minSuccessAttempt inlineResult :=? "inline success" stdout.println(`Inline retry: ${inlineResult}`)
Common mistakes
E01070 — EK9 has no break statement. For retry logic, use a for-range bounded loop with guard assignment (:=?) so the first success wins without needing break. See ek9 -h E01070 for details.
Incorrect:
break
Correct:
result :=? outcome
Other ways to ask this
- How do I retry an operation up to N times without break in EK9?
- What replaces while(true) with break for retry loops in EK9?
- How do I write a bounded retry loop in EK9?
Coming from another language?
Java: while(true) { try { result = op(); break; } catch { retries++; if (retries >= max) break; } }. Python: while True: try: result = op(); break; except: retries += 1. Rust: loop { match try_op() { Ok(r) => break r, Err(_) => retries += 1 } }. Go: for retries := 0; retries < max; retries++ { result, err := op(); if err == nil { break } }. EK9: for-range with guard on result, :=? for first-success-wins, no infinite loops possible.
Keywords: alternative, resilient, no-return, migrate, guard, null-safe, tries, no-break, loop, retry, safe, isset, maximum, fail, repeat, success, bounded, attempt