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