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
              if temp > threshold
                alert()
            case Level.Low
              monitor()

EK9 switch cases use nested if for additional conditions.

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 { ... } while condition
  EK9:    while condition
            ...

EK9 has no do-while loop. Use while with initialisation before the loop.

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) ===

      threshold <- Level.High
      for lvl in Level
        currentAction <- responseAction(lvl)
        if lvl >= threshold
          stdout.println(`ALERT: ${lvl} requires ${currentAction}`)
        else
          stdout.println(`OK: ${lvl} requires ${currentAction}`)

      // === 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 guard expressions 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?

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: guard expressions (if x <- expr()), nested if for switch conditions, stream pipelines for filtered iteration, try/finally for cleanup, comma-separated case values for fallthrough.

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