How do I migrate Swift control flow patterns to EK9?

← Control Flow · Ref: Q748

Swift developers coming to EK9 encounter five key control flow differences. Each has a direct EK9 equivalent.

SWIFT GUARD STATEMENT

  Swift:  guard let x = getValue() else { return nil }
  EK9:    if x <- getValue()
            processX(x)

EK9 has no return statement. Instead of guard-else-return, put the success path inside the if block. For complex functions, decompose into smaller functions.

SWIFT SWITCH WITH WHERE CLAUSES

  Swift:  switch value {
            case .high where temp > 100: alert()
            case .low: monitor()
          }
  EK9:    switch level
            case Level.High, Level.Critical
              if temp > threshold
                alert()
            case Level.Low, Level.Medium
              monitor()
            default
              stdout.println("Unknown level")

EK9 switch cases use nested if for additional conditions. A switch on an enumeration must cover every value (E07310) and still needs a default (E07320), which handles an unset value.

SWIFT FOR-WHERE LOOPS

  Swift:  for item in items where item.isValid {
            process(item)
          }
  EK9:    cat items | filter by isValid > stdout

Or with a for loop:

  for item in items
    if item.isValid()
      process(item)

Swift's for-where is a convenience. EK9 stream pipelines express filtering naturally.

SWIFT DEFER

  Swift:  func process() {
            let file = open()
            defer { file.close() }
            use(file)
          }
  EK9:    try
            -> resource <- openFile()
            use(resource)
          finally
            cleanup()

Or with try-with-resources for auto-close.

SWIFT REPEAT-WHILE

  Swift:  repeat { body } while condition
  EK9:    do
            body
          while condition

EK9's do-while runs the body first and checks the condition after, so the body always runs at least once. It can start with a guard (do x <- init()) and can return a value as an expression (see Q67 and Q81).

SWIFT FALLTHROUGH

  Swift:  switch x {
            case 1: fallthrough
            case 2: handle()
          }
  EK9:    switch x
            case 1, 2
              handle()

EK9 has no fallthrough. Use comma-separated case values instead.

See Q144 for the full control flow philosophy. See Q145 for break and continue replacements. See Q146 for function decomposition. See Q147 for switch fallthrough replacements. See Q148 for Java and Python migration. See Q135 for try/finally. See Q137 for try-with-resources. See Q747 for Swift optional binding migration.

Example

defines module qa.controlflow.swiftmigration

  defines type

    Level
      Low
      Medium
      High
      Critical

  defines function

    isValid() as pure
      -> item as String
      <- rtn as Boolean: item?

    <?-
      Maps a level to its response action.
    -?>
    responseAction() as pure
      -> level as Level
      <- action as String: switch level
        <- rtn as String: String()
        case Level.Low
          rtn: "monitor"
        case Level.Medium
          rtn: "investigate"
        case Level.High, Level.Critical
          rtn: "escalate"
        default
          rtn: "unknown"

  defines program

    SwiftControlFlowDemo()
      stdout <- Stdout()

      // === GUARD STATEMENT BECOMES IF GUARD ===
      // Swift: guard let action = responseAction(.High) else { return }
      // EK9: success path inside the if block

      level <- Level.High
      action <- responseAction(level)
      if action?
        stdout.println(`Action for ${level}: ${action}`)

      // === SWITCH WITH CONDITIONS (replaces Swift where clause) ===
      // Swift: case .high where temp > 100: alert()

      temperature <- 120
      alertTemperature <- 100
      for lvl in Level
        currentAction <- responseAction(lvl)
        switch lvl
          case Level.High, Level.Critical
            if temperature > alertTemperature
              stdout.println(`ALERT: ${lvl} requires ${currentAction}`)
            else
              stdout.println(`WATCH: ${lvl} at ${temperature}`)
          case Level.Low, Level.Medium
            stdout.println(`OK: ${lvl} requires ${currentAction}`)
          default
            stdout.println(`Unknown level: ${lvl}`)

      // === FOR-WHERE BECOMES STREAM PIPELINE ===
      // Swift: for item in items where item.isValid { process(item) }

      items <- ["alpha", String(), "beta", String(), "gamma"]
      cat items | filter by isValid > stdout

      // === COMMA-SEPARATED CASES (replaces Swift fallthrough) ===
      // Swift: case .High: fallthrough; case .Critical: escalate()

      testLevel <- Level.Critical
      response <- switch testLevel
        <- rtn as String: String()
        case Level.Low, Level.Medium
          rtn: "routine"
        case Level.High, Level.Critical
          rtn: "emergency"
        default
          rtn: "unclassified"
      stdout.println(`${testLevel}: ${response}`)

Common mistakes

E01072 — EK9 has no return statement. Use if guards for success paths. See ek9 -h E01072 for details.

Incorrect:

if action?
        stdout.println(`Action for ${level}: ${action}`)
      return

Correct:

if action?
        stdout.println(`Action for ${level}: ${action}`)

E50060 — String has no toString() method. responseAction() already returns a String. See ek9 -h E50060 for details.

Incorrect:

action <- responseAction(level).toString()

Correct:

action <- responseAction(level)

E50060 — Stream output target must be a stream sink, not a method call. Use > stdout to pipe output directly. See ek9 -h E50060 for details.

Incorrect:

cat items | filter by isValid > stdout.println()

Correct:

cat items | filter by isValid > stdout
Other ways to ask this
  • What is the EK9 equivalent of Swift guard statement?
  • How do I translate Swift switch with where clauses to EK9?
  • How do I replace Swift for-where loops in EK9?
  • How do I convert Swift defer to EK9?
  • What is the EK9 equivalent of Swift repeat-while?

Coming from another language?

Swift: guard-else for early exit, switch with where clauses and pattern matching, for-where filtered loops, defer for cleanup, repeat-while for do-while, fallthrough keyword in switch. Java: break, continue, return, switch fallthrough. Python: break, continue, return, no switch until match 3.10. Rust: break, continue, implicit return, match with guards. EK9: declaration guards (if x <- expr()), nested if for switch conditions, do/while for repeat-while, stream pipelines for filtered iteration, try/finally for cleanup, comma-separated case values for fallthrough.

Keywords: where, defer, fallthrough, while, swift, switch, control, flow, migrate, pattern, repeat, guard