How do I migrate complex control flow from Java or Python to EK9?

← Control Flow · Ref: Q148

Five rules map familiar control flow patterns to EK9 equivalents.

RULE 1: BREAK BECOMES HEAD

  results <- cat items | head 3 | collect as List of String

RULE 2: CONTINUE BECOMES FILTER

  valid <- cat items | filter by isNonEmpty | collect as List of String

RULE 3: EARLY RETURN BECOMES IF GUARD

  if data <- fetch()
    process(data)

RULE 4: MULTIPLE RETURNS BECOME DECOMPOSITION

  category <- categorise(amount)
  label <- describe(category)

RULE 5: FALLTHROUGH BECOMES COMMA-SEPARATED CASES

  switch code
    case 1, 2, 3
      result: "low"

THE MENTAL MODEL SHIFT

Think 'what do I want?' not 'how do I exit?'. cat items | filter by isValid | head 3 | collect expresses intent directly.

See Q50 for return variables. See Q89 for stream pipelines. See Q125 for head/tail/skip. See Q131 for Python migration. See Q132 for Java migration. See Q144 for control flow philosophy. See Q145 for break/continue replacements. See Q146 for decomposition. See Q147 for fallthrough replacements. See Q288 for break loop migration. See Q289 for return migration. See Q748 for Swift migration.

Example

defines module qa.flow.philosophy.migration

  defines function

    isNonEmpty() as pure
      -> item as String
      <- nonEmpty as Boolean: length item > 0

    isExpensive() as pure
      -> item as String
      <- expensive <- false
      minExpensiveLength <- 6
      expensive: length item > minExpensiveLength

    categorise() as pure
      -> amount as Integer
      <- category as String: "medium"
      highThreshold <- 100
      lowThreshold <- 10
      if amount >= highThreshold
        category: "high"
      else if amount < lowThreshold
        category: "low"

    describe() as pure
      -> category as String
      <- label as String: "standard item"
      switch category
        case "high"
          label: "premium item"
        case "low"
          label: "budget item"
        default
          label: "standard item"

    findByName()
      ->
        target as String
        items as List of String
      <- found as String: String()
      for item in items
        if item == target
          found: item

  defines program

    MigrationDemo()
      stdout <- Stdout()

      items <- ["", "apple", "banana", "", "cherry", "date", "elderberry", "fig", "grape"]

      // === RULE 1: BREAK -> HEAD ===

      firstThree <- cat items | filter by isNonEmpty | head 3 | collect as List of String
      stdout.println(`First 3 non-empty: ${firstThree}`)

      // === RULE 2: CONTINUE -> FILTER ===

      nonEmpty <- cat items | filter by isNonEmpty | collect as List of String
      stdout.println(`Non-empty items: ${nonEmpty}`)

      // === RULE 3: EARLY RETURN -> GUARD ===

      names <- ["Alice", "Bob", "Charlie"]

      if found <- findByName("Bob", names)
        stdout.println(`Found: ${found}`)

      if found <- findByName("Dave", names)
        stdout.println(`Found: ${found}`)
      else
        stdout.println("Dave not found")

      // === RULE 4: MULTIPLE RETURNS -> DECOMPOSITION ===

      amounts <- [5, 50, 150]
      for amount in amounts
        category <- categorise(amount)
        label <- describe(category)
        stdout.println(`Amount ${amount}: ${label}`)

      // === RULE 5: FALLTHROUGH -> COMMA CASES ===

      codes <- [1, 2, 3, 4, 5]
      for code in codes
        level <- String()
        switch code
          case 1, 2, 3
            level: "basic"
          case 4, 5
            level: "advanced"
          default
            level: "unknown"
        stdout.println(`Code ${code}: ${level}`)

      // === COMBINED: THE MENTAL MODEL SHIFT ===
      // "I want the first 2 expensive items" (not "loop, check, count, break")

      expensive <- cat items | filter by isExpensive | head 2 | collect as List of String
      stdout.println(`First 2 expensive: ${expensive}`)

Common mistakes

E01070 — EK9 has no break statement. Migration Rule 1: break becomes head. Use stream pipelines with head N instead of loop-with-break-at-count. See ek9 -h E01070 for details.

Incorrect:

firstThree <- List() of String
      for item in items
        if isNonEmpty(item)
          firstThree += item
          if length firstThree >= 3
            break

Correct:

firstThree <- cat items | filter by isNonEmpty | head 3 | collect as List of String

E01071 — EK9 has no continue statement. Migration Rule 2: continue becomes filter. Use stream pipelines with filter to keep only matching items. See ek9 -h E01071 for details.

Incorrect:

nonEmpty <- List() of String
      for item in items
        if not isNonEmpty(item)
          continue
        nonEmpty += item

Correct:

nonEmpty <- cat items | filter by isNonEmpty | collect as List of String

E01072 — EK9 has no return statement. Declare the return variable with a default value and conditionally assign. The compiler ensures all paths initialize the return variable. See ek9 -h E01072 for details.

Incorrect:

<- found as String: String()
      return found

Correct:

<- found as String: String()

E01075 — EK9 has no 'new' keyword. Types are instantiated by calling the constructor directly. Java's 'new ArrayList<>()' becomes List() of Type in EK9. See ek9 -h E01075 for details.

Incorrect:

items <- new List("", "apple", "banana", "", "cherry", "date", "elderberry", "fig", "grape")

Correct:

items <- ["", "apple", "banana", "", "cherry", "date", "elderberry", "fig", "grape"]
Other ways to ask this
  • How do I rewrite a Java loop with break and continue in EK9?
  • How do I translate early return patterns to EK9?
  • What is the step-by-step process for converting control flow to EK9?
  • What are the migration rules for converting break, continue, and return to EK9?

Coming from another language?

Java/Python/Rust/Go/JS: break, continue, return, switch fallthrough. EK9: five rules map to stream pipelines, guard expressions, function decomposition, comma-separated cases.

Keywords: rule, java, return, control, model, break, step, condition, convert, translate, rewrite, flow, pattern, continue, migration, swift, python, migrate, branch, mental