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