Question: What is the type traversal limit and why does EK9 enforce it?

What triggers E11054 excessive type traversal?

How many type transitions are allowed in a method chain?

Answer:
EK9 limits method chains to 3 type transitions. Each time a dot-chain crosses from one type to another, it counts as a transition. Exceeding 3 triggers E11054.

IMPORTANT DISTINCTION
Same-type method chains do NOT count as transitions:
  name.trim().upperCase().lowerCase()  // 0 transitions (all String)
Only CROSS-TYPE chains count:
  dept.getManager().getAddress().getCity()  // 3 transitions

WHY 3 TRANSITIONS MAXIMUM
This enforces the Law of Demeter (principle of least knowledge):
1. Structural coupling: long chains couple your code to the internal structure of distant objects
2. Fragile chains: changing any intermediate type breaks all callers
3. Testability: mocking a 4-deep chain requires 4 mock objects
4. Encapsulation violation: reaching through objects exposes internal structure

HOW TO FIX
Add a convenience method that encapsulates the traversal:
  // Before: dept.getManager().getAddress().getRegion().getCity()
  // After:  dept.getManagerCity()
  // The Department class adds: getManagerCity() = getManager().getAddress().getRegion().getCity()

THIS EXAMPLE
The method chain below crosses exactly 3 type boundaries (Department to Manager to Address to String). The Address class also has a getRegion() method returning a Region object. Adding that extra traversal step to reach the city via region would push from 3 to 4 transitions, triggering E11054.

See Q310 for code quality overview. See Q696 for complexity limits. See Q322 for quality enforcement.

defines module qa.codequality.typetraversalboundary

  defines class

    <?-
      Geographic region containing a city name.
    -?>
    Region
      regionCity <- String()

      Region()
        -> regionCity as String
        this.regionCity: regionCity

      getCity() as pure
        <- rtn as String: regionCity

      default operator ?

    <?-
      Address that belongs to a geographic region.
      Has both getCity() for direct access and getRegion() for region traversal.
    -?>
    Address
      cityLabel <- String()
      addressRegion <- Region()

      Address()
        ->
          cityLabel as String
          addressRegion as Region
        this.cityLabel: cityLabel
        this.addressRegion: addressRegion

      getCity() as pure
        <- rtn as String: cityLabel

      getRegion() as pure
        <- rtn as Region: addressRegion

      default operator ?

    <?-
      Manager type that holds an address.
    -?>
    Manager
      managerName <- String()
      homeAddress <- Address()

      Manager()
        ->
          managerName as String
          homeAddress as Address
        this.managerName: managerName
        this.homeAddress: homeAddress

      getName() as pure
        <- rtn as String: managerName

      getAddress() as pure
        <- rtn as Address: homeAddress

      default operator ?

    <?-
      Department type that holds a manager.
      Method chains from Department cross 3 type boundaries maximum.
    -?>
    Department
      deptName <- String()
      deptManager <- Manager()

      Department()
        ->
          deptName as String
          deptManager as Manager
        this.deptName: deptName
        this.deptManager: deptManager

      getName() as pure
        <- rtn as String: deptName

      getManager() as pure
        <- rtn as Manager: deptManager

      default operator ?

  defines program

    TypeTraversalBoundaryDemo()
      stdout <- Stdout()

      addressRegion <- Region("Springfield")
      homeAddress <- Address(cityLabel: "Springfield", addressRegion: addressRegion)
      deptManager <- Manager(managerName: "Alice", homeAddress: homeAddress)
      department <- Department(deptName: "Engineering", deptManager: deptManager)

      //3 type transitions: Department -> Manager -> Address -> String
      //This is exactly at the boundary
      managerCity <- department.getManager().getAddress().getCity()
      stdout.println(managerCity)

      //Same-type chains: 0 transitions (all String)
      processed <- managerCity.trim().upperCase()
      stdout.println(processed)

      //2 transitions: Department -> Manager -> String (within limit)
      managerLabel <- department.getManager().getName()
      stdout.println(managerLabel)

Java: no type traversal limit (Demeter checked by optional tools). Python: no limit. C++: no limit. Rust: method chains common, no cross-type limit. Go: no limit (short method chains by convention). EK9: 3-transition hard limit enforced at compile time.

Demeter E11054 boundary chain clean-code coupling dot encapsulation limit quality transition traversal type

Explanation: Adding an extra type transition (Address to Region to String instead of Address to String) pushes the chain from 3 to 4 type transitions, exceeding the limit. Encapsulate the traversal in a convenience method. See ek9 -h E11054 for details.


Question: Why must every constructor, operator, and pure function return be captured?

What triggers E11050 discarded operator return?

What triggers E11055 discarded constructor return?

What triggers E11051 discarded pure function return?

When can I ignore a function return value?

Answer:
EK9 requires that return values from constructors, computational operators, and pure functions are always captured. There is no threshold: discarding any of these is always an error.

THIS IS A DESIGN RULE, NOT A THRESHOLD
Unlike nesting depth or inheritance depth, this check has no numeric boundary. It is binary: either the return is captured or it is not.

WHY EACH CATEGORY IS ENFORCED

CONSTRUCTOR RETURNS (E11055)
A constructor creates an object. Calling it as a bare statement means the object is immediately garbage collected. This is always either a bug (forgot the assignment) or a misuse.

OPERATOR RETURNS (E11050)
Computational operators (+, -, *, /) produce new values without modifying the original. Discarding the result means the computation was wasted work.

PURE FUNCTION RETURNS (E11051)
Pure functions have no side effects. If the return is discarded, the entire call did nothing observable. This is dead code.

RESULT/OPTIONAL RETURNS (E11052)
Result and Optional types exist to force error handling. Discarding them defeats their purpose, like catching an exception and silently ignoring it.

WHEN RETURNS CAN BE IGNORED
Impure functions CAN be called for their side effects:
  stdout.println("hello")  // Impure: called for side effect, return (Void) is fine to ignore
  list += item              // Mutation operator: modifies list in place

See Q316 for discarded returns overview. See Q693 for captured operator returns. See Q310 for code quality overview.

defines module qa.codequality.discardedreturnboundary

  defines function

    <?-
      Pure function: return value must always be captured.
    -?>
    sanitizeInput() as pure
      -> rawInput as String
      <- cleaned as String: rawInput.trim()

    <?-
      Pure function: validates input length.
    -?>
    checkLength() as pure
      -> inputText as String
      <- isValid as Boolean: length inputText > 0

  defines class

    <?-
      Simple sensor class demonstrating constructor capture.
    -?>
    Sensor
      reading <- Float()

      Sensor()
        -> reading as Float
        this.reading: reading

      getReading() as pure
        <- rtn as Float: reading

      default operator ?

  defines program

    DiscardedReturnBoundaryDemo()
      stdout <- Stdout()

      //Correct: constructor return captured
      temperatureSensor <- Sensor(98.6)
      stdout.println($temperatureSensor.getReading())

      //Correct: pure function return captured
      trimmedInput <- sanitizeInput("  hello  ")
      stdout.println(trimmedInput)

      //Correct: pure function return captured (independent variable)
      isValid <- checkLength("test")
      stdout.println(`Valid: ${isValid}`)

      //Impure: println called for side effect, OK to not capture
      stdout.println("done")

Java: all return values silently discardable. Rust: #[must_use] attribute (opt-in, suppressible). Go: blank identifier _ explicitly discards. Python: no enforcement. C++: [[nodiscard]] attribute (opt-in). EK9: automatic enforcement for all constructors, pure functions, computational operators, and Result/Optional returns.

E11050 E11051 E11052 E11055 boundary capture clean-code code constructor dead discard operator pure quality return

      Sensor(98.6)
      stdout.println("98.6")

Explanation: Constructor return discarded. The Sensor object is created then immediately garbage collected. Assign it to a variable. See ek9 -h E11055 for details.

      sanitizeInput("  hello  ")
      stdout.println("hello")

Explanation: Pure function sanitizeInput has no side effects. Discarding its return value makes the call completely useless. See ek9 -h E11051 for details.


Question: What is the combined complexity-size check and when does it trigger?

What triggers E11020 combined complexity size?

How do complexity and size interact in EK9?

What is the combined complexity threshold?

Why does EK9 check both complexity and size together?

Answer:
EK9 checks that functions are not both moderately complex AND moderately large at the same time. A function can be somewhat complex OR somewhat long, but not both.

FORMULA
(complexity / maxComplexity) x (statements / maxStatements) must be 0.50 or less

For functions: maxComplexity = 45, maxStatements = 150
For methods: maxComplexity = 45, maxStatements = 100
For operators: maxComplexity = 45, maxStatements = 50

WHY THIS CHECK EXISTS
Individual complexity and statement count checks catch extreme cases. But a function can pass both individually while still being a maintenance problem:
- 30 complexity (passes: under 45) + 120 statements (passes: under 150)
- Combined: (30/45) x (120/150) = 0.67 x 0.80 = 0.53 (FAILS: over 0.50)

THIS IS A DESIGN INSIGHT
A function that is 67% of max complexity AND 80% of max size is carrying too much responsibility. Neither metric alone triggers a violation, but together they reveal a function that needs decomposition.

HOW TO FIX
Decompose into smaller functions. Reducing EITHER complexity (fewer branches) OR size (fewer statements) brings the combined ratio under the threshold.

THIS EXAMPLE
The analyzeMetrics function below has moderate complexity and moderate size, but the combined ratio stays well under 0.50 because the logic is decomposed into helper functions. A function that triggers E11020 would need 30+ branches AND 100+ statements simultaneously, which is too large for a concise example but exactly the kind of monolithic function this check prevents.

See Q696 for complexity limits. See Q310 for code quality overview. See Q322 for quality enforcement.

defines module qa.codequality.combinedcomplexityboundary

  defines constant

    THRESHOLD_LOW <- 25
    THRESHOLD_MEDIUM <- 50
    THRESHOLD_HIGH <- 75
    THRESHOLD_EXCELLENT <- 90

  defines function

    <?-
      Classifies a numeric score into a category.
      Small, focused function: low complexity, few statements.
    -?>
    classifyLevel() as pure
      -> scoreValue as Integer
      <- scoreLevel as String: "unknown"

      if scoreValue < THRESHOLD_LOW
        scoreLevel: "low"
      else if scoreValue < THRESHOLD_MEDIUM
        scoreLevel: "medium"
      else if scoreValue < THRESHOLD_HIGH
        scoreLevel: "high"
      else
        scoreLevel: "excellent"

    <?-
      Generates a description from a classified level.
      Separate function keeps both complexity and size low.
    -?>
    describeLevel() as pure
      -> scoreLevel as String
      <- classification as String: "unclassified"

      if scoreLevel == "low"
        classification: "needs improvement"
      else if scoreLevel == "medium"
        classification: "average performer"
      else if scoreLevel == "high"
        classification: "strong performer"
      else if scoreLevel == "excellent"
        classification: "outstanding"

    <?-
      Analyzes metrics by composing small focused functions.
      Combined complexity-size stays well under 0.50 because
      logic is decomposed into classifyLevel and describeLevel.
    -?>
    analyzeMetrics() as pure
      -> scoreValue as Integer
      <- report as String: ""

      scoreLevel <- classifyLevel(scoreValue)
      classification <- describeLevel(scoreLevel)
      passed <- scoreValue >= THRESHOLD_MEDIUM

      if passed
        report: `${classification} (${scoreLevel}) PASS`
      else
        report: `${classification} (${scoreLevel}) FAIL`

  defines program

    CombinedComplexityBoundaryDemo()
      stdout <- Stdout()

      scores <- [15, 42, 78, 95]
      for scoreValue in scores
        stdout.println(analyzeMetrics(scoreValue))

Java: no combined check (SonarQube checks complexity and length separately). Python: no combined check. C++: no combined check. Rust: no combined check. Go: no combined check. EK9: multiplicative combined check catches functions that pass individual limits but are still too complex for their size.

E11020 boundary clean-code combined complexity decompose formula limit metric quality size statements threshold

Explanation: String has no toUpperCase() method in EK9. Use upperCase() instead. See ek9 -h E50060 for details.


Question: How does EK9 detect redundant Boolean comparisons and logical tautologies?

What is E08084 redundant Boolean comparison in EK9?

What is E08085 logical tautology in EK9?

Why does EK9 reject comparing a Boolean to true or false?

Answer:
EK9 detects two categories of pointless Boolean logic at compile time.

REDUNDANT BOOLEAN COMPARISON (E08084)
Comparing a Boolean expression directly to true or false is always redundant. 'if flag == true' is identical to 'if flag'. 'if flag == false' is identical to 'if not flag'. The comparison adds nothing and obscures intent.

LOGICAL TAUTOLOGY (E08085)
An expression like 'flag or not flag' is always true regardless of what flag is. Similarly 'flag and not flag' is always false. These are structural tautologies or contradictions that indicate a bug, typically a copy-paste error where two different variables were intended.

CORRECT PATTERNS
Use Boolean values directly in conditions:
  if isReady             not 'if isReady == true'
  if not isDone          not 'if isDone == false'
  if ready or fallback   different variables, not 'ready or not ready'

See Q318 for self-comparison detection. See Q558 for tautological conditions overview. See Q639 for Boolean patterns without literals.

defines module qa.codequality.booleantautology

  defines function

    checkEligibility() as pure
      ->
        hasAccount as Boolean
        isVerified as Boolean
      <- eligible as Boolean: hasAccount and isVerified

    canProceed() as pure
      ->
        ready as Boolean
        fallback as Boolean
      <- proceed as Boolean: ready or fallback

    describeState() as pure
      ->
        active as Boolean
        locked as Boolean
      <- description as String: "unknown"

      if active and not locked
        description: "available"
      else if active
        description: "locked"
      else if not active
        description: "inactive"

  defines program

    BooleanTautologyDemo()
      stdout <- Stdout()

      eligible <- checkEligibility(hasAccount: true, isVerified: true)
      stdout.println(`Eligible: ${eligible}`)

      proceed <- canProceed(ready: false, fallback: true)
      stdout.println(`Proceed: ${proceed}`)

      stdout.println(describeState(active: true, locked: false))
      stdout.println(describeState(active: true, locked: true))
      stdout.println(describeState(active: false, locked: false))

Java: SpotBugs detects some redundant Boolean comparisons. ESLint has no-constant-binary-expression. Rust: clippy has bool_comparison and logic_bug. Python: pylint detects some redundant comparisons. EK9: mandatory compiler error for both redundant Boolean comparisons and logical tautologies.

E08084 E08085 boolean comparison contradiction false logic quality redundant tautology true

eligible <- checkEligibility(hasAccount: true, isVerified: true).booleanValue()

Explanation: Boolean has no booleanValue() method in EK9. Boolean values are used directly. See ek9 -h E50060 for details.

stdout.println(describeState(active: true, locked: false).toUpperCase())


Question: How does EK9 detect conditions that are always true or always false?

What is E08086 condition always true in EK9?

What is E08087 condition always false in EK9?

How does EK9 find dead code from constant conditions?

Answer:
EK9 tracks variable assignments through code paths and detects when a condition is guaranteed to produce the same result every time.

CONDITION ALWAYS TRUE (E08086)
If you assign a literal and then compare to the same value, the compiler knows the result. 'x <- 5; if x == 5' is always true. The else branch is dead code. Also detected when an outer condition implies an inner one: 'if count > 10; if count > 6' is always true since count > 10 implies count > 6.

CONDITION ALWAYS FALSE (E08087)
Similarly, 'x <- 5; if x == 10' is always false. The if body is dead code. Also detected for contradictory ranges: 'if count > 10 and count < 3' is always false.

WHEN TRACKING RESETS
The compiler stops tracking when a variable is reassigned from a function call or mutated by a non-pure call, since the value becomes unknown.

See Q557 for dead code detection overview. See Q558 for tautological conditions.

defines module qa.codequality.conditiontruth

  defines function

    classifyScore() as pure
      -> score as Integer
      <- label as String: "average"

      highThreshold <- 90
      lowThreshold <- 40

      if score >= highThreshold
        label: "excellent"
      else if score < lowThreshold
        label: "poor"

    categorizeRange() as pure
      -> measurement as Integer
      <- category as String: "normal"

      upperBound <- 100
      lowerBound <- 10

      if measurement > upperBound
        category: "above"
      else if measurement < lowerBound
        category: "below"

  defines program

    ConditionTruthDemo()
      stdout <- Stdout()

      stdout.println(classifyScore(95))
      stdout.println(classifyScore(30))
      stdout.println(classifyScore(60))

      stdout.println(categorizeRange(150))
      stdout.println(categorizeRange(5))
      stdout.println(categorizeRange(50))

Java: SpotBugs detects some constant conditions. C/C++: compiler warnings for tautological comparisons with -Wtautological-compare. Rust: clippy detects some always-true conditions. Go: go vet detects some unreachable code. EK9: mandatory compiler error with full flow-sensitive tracking across branches.

E08086 E08087 always code condition constant dead false flow quality tracking true


Question: How does EK9 detect redundant empty and isSet checks?

What is E08089 isSet always false in EK9?

What is E08092 empty always true in EK9?

What is E08093 empty always false in EK9?

Answer:
EK9 tracks variable state through data flow and detects three categories of redundant checks.

ISSET ALWAYS FALSE (E08089)
If a variable is declared unset (with ?) and never assigned a value, checking it with ? is always false. The code inside the if block is dead code.

EMPTY ALWAYS TRUE (E08092)
If a collection is created with no elements and not modified before an empty check, the check is always true. An empty Optional is also always empty.

EMPTY ALWAYS FALSE (E08093)
If a collection is created from a literal with elements, an empty check is always false. An Optional created with a value is never empty.

CORRECT PATTERNS
Assign a value before checking isSet. Populate collections before checking empty. Use function returns where the state is genuinely unknown.

See Q559 for redundant isSet overview. See Q640 for collection isSet patterns.

defines module qa.codequality.emptyissetflow

  defines function

    findName()
      -> key as String
      <- result as String: String()

      if key == "admin"
        result: "Administrator"

    describeItems()
      -> items as List of String
      <- description as String: "empty"

      if not items empty
        description: `${length items} items`

  defines program

    EmptyIsSetFlowDemo()
      stdout <- Stdout()

      //isSet check on function return - genuinely needed
      name <- findName("admin")
      if name?
        stdout.println(`Found: ${name}`)

      //empty check on function return - genuinely needed
      items <- ["alpha", "beta"]
      stdout.println(describeItems(items))

      emptyList <- List() of String
      stdout.println(describeItems(emptyList))

Java: no built-in detection, optional IDE inspections. Rust: clippy detects some redundant Option checks. Python: pylint detects some unreachable code. Go: go vet detects some unreachable code. EK9: mandatory compiler error with full data flow tracking for isSet and empty checks.

E08089 E08092 E08093 always code collection dead empty flow isset never optional quality redundant

Explanation: String has no getValue() method. findName() already returns a String. See ek9 -h E50060 for details.

Explanation: List has no toString() method. Use string interpolation or the $ operator. See ek9 -h E50060 for details.


Question: Why does EK9 reject reassignment in pure functions and parameter reassignment?

What is E08100 reassignment in pure context?

What is E08110 parameter reassignment in EK9?

How do I modify values in a pure function?

Answer:
EK9 enforces two reassignment restrictions to prevent hidden state changes.

PURE CONTEXT REASSIGNMENT (E08100)
Pure functions cannot use ':=' to reassign variables. Pure functions should compute results, not accumulate state. Use ':=?' (guarded assignment, only sets if unset) or restructure the logic to use return variable assignment.

PARAMETER REASSIGNMENT (E08110)
Function and method parameters are read-only. You cannot reassign a parameter with ':='. Parameters represent the caller's input and modifying them obscures the data flow. Instead, copy the parameter to a local variable and modify the copy.

WHY ENFORCED
These restrictions exist because reassignment creates hidden state changes that are hard to reason about. Pure functions should be deterministic and side-effect free. Parameter immutability prevents confusion about whether modifications are visible to the caller.

See Q678 for purity contracts. See Q635 for pure mutation restrictions.

defines module qa.codequality.pureparamreassign

  defines function

    classifyScore() as pure
      -> score as Integer
      <- label as String: "average"

      highThreshold <- 90
      lowThreshold <- 40

      if score >= highThreshold
        label: "excellent"
      else if score < lowThreshold
        label: "poor"

    doubleValue() as pure
      -> input as Integer
      <- result as Integer: input * 2

    formatName()
      -> rawName as String
      <- formatted as String: rawName

      localName <- rawName
      localName := "Mr. " + localName
      formatted: localName

  defines program

    PureParamReassignDemo()
      stdout <- Stdout()

      stdout.println(classifyScore(95))
      stdout.println(classifyScore(30))
      stdout.println(classifyScore(60))

      stdout.println(`Doubled: ${doubleValue(21)}`)
      stdout.println(formatName("Smith"))

Java: parameters can be reassigned (but final prevents it). Kotlin: function parameters are val (immutable) by default. Rust: parameters are immutable by default, require mut. Python: parameters can be reassigned freely. Go: parameters can be reassigned freely. EK9: parameters always immutable, pure context bans all reassignment.

E08100 E08110 context immutable parameter pure quality readonly reassignment restriction

Explanation: Parameters are read-only in EK9. Copy the parameter to a local variable first, then modify the copy. See ek9 -h E08110 for details.

Explanation: String has no toString() method. classifyScore() already returns a String. See ek9 -h E50060 for details.


Question: Why does EK9 reject mutation operators on function types?

What is E08240 mutation on function type?

Why can I not use copy operator on a function variable?

What operators work on function types in EK9?

Answer:
Function types are callable references, not data containers. Mutation operators like :=: (copy), :~: (merge), :^: (replace), += (add-assign), and -= (subtract-assign) are not applicable to function types.

WHAT WORKS ON FUNCTIONS
Only these operations are valid on function variables:
- := (assignment) changes which function is referenced
- ? (isSet) checks if a function reference has been assigned

WHY MUTATION IS BANNED
Functions represent behaviour, not state. Copying, merging, or replacing functions has no meaningful semantics. Assignment (:=) changes the reference to point at a different function. That is fundamentally different from :=: which copies the internal state of an object.

COMMON MISTAKE
Developers coming from languages where lambdas are objects (Java, Python) try to use copy semantics on function references. In EK9, simply use := to reassign a function variable to a different function.

See Q49 for function basics. See Q52 for dynamic functions. See Q596 for function vs method distinction.

defines module qa.codequality.functionmutation

  defines function

    Formatter as abstract
      -> text as String
      <- result as String?

    upperFormatter() is Formatter
      -> text as String
      <- result as String: text.upperCase()

    bracketFormatter() is Formatter
      -> text as String
      <- result as String: `[${text}]`

    applyFormatter()
      ->
        formatter as Formatter
        text as String
      <- result as String: formatter(text)

  defines program

    FunctionMutationDemo()
      stdout <- Stdout()

      //Function assignment with :=
      handler as Formatter: upperFormatter
      stdout.println(applyFormatter(handler, "hello"))

      //Reassignment with := is valid
      handler := bracketFormatter
      stdout.println(applyFormatter(handler, "world"))

      //isSet check with ? is valid
      stdout.println(`Handler set: ${handler?}`)

Java: lambdas are objects with copy semantics via clone. Python: functions are objects that can be copied. JavaScript: functions are objects with spread/assign. Rust: closures implement traits, can be cloned if contents are Clone. EK9: function types are pure references, only := and ? are valid.

E08240 callable copy function merge mutation operator quality reference replace type

Explanation: Function types do not support the copy operator :=:. Functions are references, not data containers. Use := (assignment) to change which function is referenced. See ek9 -h E08240 for details.

handler as Formatter: upperFormatter
      handler :~: bracketFormatter

Explanation: Function types do not support the merge operator :~:. Use := (assignment) to reassign the function reference. See ek9 -h E08240 for details.

handler :^: bracketFormatter
      stdout.println(applyFormatter(handler, "hello"))

Explanation: Function types do not support the replace operator :^:. Use := (assignment) to change the function reference. See ek9 -h E08240 for details.


Question: How does EK9 detect duplicate names and properties in type hierarchies?

What is E02020 duplicate property for JSON in EK9?

Why does EK9 reject properties with the same name in parent and child?

Answer:
EK9 detects two categories of property naming collisions in type hierarchies at compile time.

DUPLICATE NAME (E02010)
A child type cannot redeclare a property with the same name as a parent property. This prevents field shadowing which causes ambiguity in method resolution and state management.

DUPLICATE PROPERTY FOR JSON (E02020)
When a type hierarchy has two properties with the same name at different levels and uses the JSON serialization operator ($$), the compiler cannot determine which to include. EK9 rejects this at compile time rather than producing ambiguous JSON output.

CORRECT PATTERNS
Use distinct names for properties at each level. If a child needs different data, use a different property name. If the parent property is sufficient, access it through inherited methods.

See Q93 for class definition. See Q97 for records vs classes.

defines module qa.codequality.duplicatenames

  defines class

    Employee
      name <- String()
      role <- String()

      Employee()
        ->
          name as String
          role as String
        this.name: name
        this.role: role

      describe()
        <- rtn as String: `${name} (${role})`

      default operator

  defines program

    DuplicateNamesDemo()
      stdout <- Stdout()

      emp <- Employee("Alice", "Engineer")
      stdout.println(emp.describe())
      stdout.println(`Equal: ${emp == Employee("Alice", "Engineer")}`)

Java: field shadowing is allowed but produces warnings. Python: attribute shadowing is implicit. Rust: no inheritance, no shadowing. Go: embedding can shadow fields. Kotlin: property override requires explicit 'override' keyword. EK9: duplicate names rejected at compile time, no shadowing allowed.

E02010 E02020 collision duplicate hierarchy json name property quality shadow

Explanation: EK9 has no toString() method. Use string interpolation or the class's describe() method. See ek9 -h E50060 for details.

emp <- Employee("Alice", "Engineer").getName()

Explanation: Employee has no getName() method. Use describe() to get employee information. See ek9 -h E50060 for details.


Question: How does EK9 detect duplicate trait references and service path conflicts?

What is E02050 duplicate trait reference in EK9?

What is E02070 service HTTP path duplicated in EK9?

What is E02080 delegate and method name clash in EK9?

Answer:
EK9 detects three categories of structural duplication at compile time.

DUPLICATE TRAIT REFERENCE (E02050)
Listing the same trait multiple times in a 'with trait of' clause is redundant and indicates a mistake. Each trait should appear exactly once.

SERVICE PATH DUPLICATION (E02070)
Two service operations cannot share identical HTTP method and path pattern combinations. Even if the path variable names differ, the routing structure is the same and creates ambiguity.

DELEGATE METHOD NAME CLASH (E02080)
A function delegate field cannot have the same name as a method in the same class. The compiler cannot distinguish between calling the delegate and calling the method.

See Q93 for class traits. See Q657 for service URI mapping.

defines module qa.codequality.duplicatetraits

  defines trait

    Printable
      show() as abstract
        <- rtn as String?

    Describable
      describe() as abstract
        <- rtn as String?

  defines class

    Item with trait of Printable, Describable
      name <- String()

      Item()
        -> name as String
        this.name: name

      override show()
        <- rtn as String: name

      override describe()
        <- rtn as String: `Item: ${name}`

      default operator

  defines program

    DuplicateTraitsDemo()
      stdout <- Stdout()

      item <- Item("Widget")
      stdout.println(item.show())
      stdout.println(item.describe())

Java: duplicate interface implementation silently ignored. Python: no trait concept, multiple inheritance. Rust: duplicate trait bounds produce errors. Go: duplicate interface embedding is valid. Spring Boot: duplicate path mappings fail at runtime. EK9: all three detected at compile time.

E02050 E02070 E02080 clash delegate duplicate method path quality service trait

Explanation: The same trait 'Printable' is listed twice. Each trait should appear only once in the 'with trait of' clause. See ek9 -h E02050 for details.

Explanation: EK9 has no toString() method. Use the show() method or string interpolation. See ek9 -h E50060 for details.

Explanation: The method is describe(), not getDescription(). EK9 does not use Java-style getter naming. See ek9 -h E50060 for details.


Question: How does EK9 detect reference conflicts and unresolved references?

What is E03020 references conflict in EK9?

What is E03030 reference does not resolve in EK9?

Why does my module reference fail to compile?

Answer:
EK9 validates all module references at compile time and detects two categories of problems.

REFERENCES CONFLICT (E03020)
The same symbol cannot be referenced twice in a references block. Each external symbol should appear exactly once. Duplicating a reference serves no purpose and may indicate a copy-paste error.

REFERENCE DOES NOT RESOLVE (E03030)
A reference must point to an existing module and symbol. If the module path or symbol name is misspelled, or the module does not exist, the compiler rejects the reference. This catches typos and stale references early.

CORRECT PATTERNS
Ensure each reference appears once and points to a valid module::Symbol. Use fully qualified inline access as an alternative to the references block.

See Q726 for module reference syntax. See Q142 for cross-module constants.

defines module qa.codequality.referenceconflicts

  defines function

    formatItem() as pure
      -> text as String
      <- rtn as String: `[${text}]`

  defines program

    ReferenceConflictsDemo()
      stdout <- Stdout()

      //Direct function call within same module
      result <- formatItem("hello")
      stdout.println(result)
      stdout.println(formatItem("world"))

Java: duplicate imports produce warnings. Python: duplicate imports silently overwrite. Rust: duplicate use statements produce errors. Go: unused imports are errors. EK9: duplicate references and unresolved references are both compile-time errors.

E03020 E03030 conflict duplicate import missing module quality reference resolve

Explanation: String has no getValue() method. formatItem() already returns a String. See ek9 -h E50060 for details.

Explanation: String has no toString() method in EK9. The function already returns a String. See ek9 -h E50060 for details.


Question: How does EK9 detect when a function type or generic type is used incorrectly?

What is E04040 type must be function in EK9?

Why does EK9 reject my stream call on a non-function type?

Answer:
EK9 validates type usage in two specific contexts.

TYPE MUST BE FUNCTION (E04040)
The 'call' and 'async' stream operators require function types. If you pipe non-function values (like integers or strings) into a call operation, the compiler rejects it. Only function references can be called.

NOT A TEMPLATE (E04070)
The 'of' syntax (e.g., List of String) is only valid for generic/template types. Applying type parameters to a non-generic type like Integer or a user-defined non-generic class is an error.

CORRECT PATTERNS
For stream call operations, ensure the pipeline contains function references. For generic types, only use 'of' with types designed to be parameterized (List, Dict, Optional, Result, etc.).

See Q707 for consumer/acceptor patterns. See Q642 for generic constructor inference.

defines module qa.codequality.typefunctiongeneric

  defines function

    formatUpper() as pure
      -> text as String
      <- rtn as String: text.upperCase()

    formatBracket() as pure
      -> text as String
      <- rtn as String: `[${text}]`

  defines program

    TypeFunctionGenericDemo()
      stdout <- Stdout()

      //Correct use of generic types
      names <- List() of String
      names += "Alice"
      names += "Bob"
      stdout.println(`Names: ${length names}`)

      //Functions used correctly
      stdout.println(formatUpper("hello"))
      stdout.println(formatBracket("world"))

Java: type safety on generics via erasure, runtime ClassCastException possible. Python: no compile-time checking. Rust: trait bounds enforce generic constraints. Go: type parameters require interface constraints. EK9: compile-time enforcement of function types and generic type parameters.

E04040 E04070 call function generic parameter quality stream template type

Explanation: String has no getValue() method. formatUpper() already returns a String. See ek9 -h E50060 for details.

Explanation: List is a generic type that requires a type parameter. Use 'List() of String' for explicit typing. See ek9 -h E06010 for details.


Question: What are the restrictions on this and super in EK9?

What is E05040 super for Any not required in EK9?

What is E05090 inappropriate use of this or super in EK9?

Why does EK9 reject super() in my constructor?

Answer:
EK9 enforces specific rules about how 'this' and 'super' can be used.

SUPER FOR ANY NOT REQUIRED (E05040)
All EK9 classes implicitly extend 'Any'. Calling super() in a constructor when there is no explicit parent class is unnecessary and rejected. Only call super() when the class explicitly extends another class.

INAPPROPRIATE USE OF THIS OR SUPER (E05090)
You cannot directly assign to 'this' or 'super' using ':=', ':=?', or ':'. These would attempt to replace the object pointer itself, which is not meaningful. Use ':=:' (copy) or ':~:' (merge) instead to copy state from another object.

ALLOWED OPERATIONS
- this :=: other  (copy state from other)
- this :~: other  (merge state from other)
- $this           (string conversion)
- this?           (isSet check)

See Q580 for this delegation. See Q581 for super delegation.

defines module qa.codequality.thissuperrestrictions

  defines class

    Base as open
      label <- String()

      Base()
        -> label as String
        this.label: label

      describe()
        <- rtn as String: label

      default operator

    Child extends Base
      role <- String()

      Child()
        ->
          label as String
          role as String
        //super() IS valid here because Child explicitly extends Base
        super(label)
        this.role: role

      override describe()
        <- rtn as String: `${super.describe()} (${role})`

      default operator

  defines program

    ThisSuperDemo()
      stdout <- Stdout()

      child <- Child("Alice", "Engineer")
      stdout.println(child.describe())

      base <- Base("Bob")
      stdout.println(base.describe())

Java: super() implicitly inserted if omitted. Python: super().__init__() must be called explicitly. Rust: no super concept. Go: no inheritance. Kotlin: super() implicitly called. EK9: super() only valid with explicit parent, this/super cannot be reassigned.

E05040 E05090 assignment constructor copy merge quality restriction super this

Base()
        -> label as String
        super()
        this.label: label

Explanation: Base has no explicit parent, so calling super() is unnecessary. All classes implicitly extend Any. Remove the super() call. See ek9 -h E05040 for details.

Explanation: EK9 has no toString() method. Use the describe() method or string interpolation. See ek9 -h E50060 for details.

Explanation: The method is describe(), not getLabel(). EK9 does not use Java-style getter naming. See ek9 -h E50060 for details.

Explanation: Cannot assign directly to 'this'. Use 'this.fieldName' to access a specific field, or use :=:, :~:, += etc. for aggregate operations. See ek9 -h E05090 for details.


Question: How does EK9 validate generic constructors and sealed type allow only lists?

What is E05200 incompatible genus constructor in EK9?

What is E05250 abstract in allow only in EK9?

Why does EK9 reject abstract classes in sealed trait allow only lists?

Answer:
EK9 validates two specific type constraint patterns at compile time.

INCOMPATIBLE GENUS CONSTRUCTOR (E05200)
When instantiating a generic type, the constructor arguments must be compatible with the type parameters. If the generic type requires a specific kind of constructor (e.g., a copy constructor from the type parameter), the actual arguments must satisfy that constraint.

ABSTRACT IN ALLOW ONLY (E05250)
Sealed traits use 'allow only' to restrict which concrete types can implement them. Abstract classes cannot appear in this list because they can never be instantiated as concrete runtime types. Only concrete classes that can actually exist at runtime belong in the allow only list.

CORRECT PATTERNS
For generics, ensure constructor arguments match type parameter requirements. For sealed traits, list only concrete classes in the allow only clause.

See Q679 for sealed allow only. See Q642 for generic constructor inference.

defines module qa.codequality.genericsealedconstraints

  defines class

    ConcreteAlpha
      doWork()
        content <- "Alpha"
        require content?

      default operator

    ConcreteBeta
      doWork()
        content <- "Beta"
        require content?

      default operator

  defines trait

    //Correct: only concrete classes in allow only list
    Processable allow only ConcreteAlpha, ConcreteBeta
      process() as abstract

  defines program

    GenericSealedDemo()
      stdout <- Stdout()

      //Correct generic usage
      names <- List() of String
      names += "Alice"
      names += "Bob"
      stdout.println(`Names: ${length names}`)

Java: generic constraints via bounded wildcards, no sealed trait equivalent until Java 17. Kotlin: sealed classes restrict subclassing. Rust: trait bounds on generics. Swift: no sealed traits. EK9: compile-time enforcement of both generic constructor compatibility and sealed trait allow only lists.

E05200 E05250 abstract allow constraint constructor generic only quality sealed

Processable allow only ConcreteAlpha, AbstractWorker, ConcreteBeta

Explanation: AbstractWorker does not exist in this module. Adding an unresolved type to the allow only list triggers E50010. See ek9 -h E50010 for details.


Question: How does EK9 validate function and method parameters?

What is E06250 named parameters must match in EK9?

What is E06270 function parameter mismatch in EK9?

What is E06310 require no arguments in EK9?

Answer:
EK9 validates function and method parameters comprehensively at compile time.

NAMED PARAMETERS MUST MATCH (E06250)
When using named parameters in a call, the names must match the parameter names in the function definition. Misspelled or incorrect parameter names are rejected.

FUNCTION PARAMETER MISMATCH (E06270)
Argument types must match parameter types. EK9 has no implicit type conversions. Passing an Integer where a String is expected is a compile-time error. Use explicit conversion operators like $ for string conversion.

REQUIRE NO ARGUMENTS (E06310)
Stream call and async operators require zero-argument supplier functions. Piping a function that takes parameters into a call operator is rejected.

INVALID NUMBER OF PARAMETERS (E06320)
Dispatcher overloads must have matching parameter counts. All handler methods must accept the same number of parameters as the dispatcher entry point.

See Q694 for named arguments patterns. See Q596 for function parameters.

defines module qa.codequality.parametervalidation

  defines function

    greet() as pure
      -> name as String
      <- rtn as String: `Hello, ${name}`

    createUser() as pure
      ->
        name as String
        age as Integer
      <- rtn as String: `${name} (age ${age})`

    doubleIt() as pure
      -> number as Integer
      <- rtn as Integer: number * 2

  defines program

    ParameterValidationDemo()
      stdout <- Stdout()

      //Correct parameter types
      stdout.println(greet("Alice"))
      stdout.println(createUser("Bob", 25))
      stdout.println(`Doubled: ${doubleIt(21)}`)

      //Correct: explicit conversion with $
      count <- 42
      stdout.println(greet($count))

Java: implicit widening conversions allowed. Python: duck typing, no compile-time checks. Rust: no implicit conversions, explicit Into/From traits. Go: no implicit conversions. Kotlin: limited implicit conversions. EK9: zero implicit conversions, all parameter types checked at compile time.

E06250 E06270 E06310 E06320 argument conversion mismatch named parameter quality type validation

Explanation: Function 'greet' expects a String but received an Integer. EK9 has no implicit type conversion. Use the $ operator to convert to String explicitly. See ek9 -h E06270 for details.

Explanation: String has no toUpperCase() method. Use upperCase() instead. See ek9 -h E50060 for details.

Explanation: String has no toString() method. createUser() already returns a String. See ek9 -h E50060 for details.

Explanation: Integer has no toString() method. Use string interpolation or the $ operator. See ek9 -h E50060 for details.


Question: How do I migrate Swift enum associated values to EK9?

What is the EK9 equivalent of Swift enum with associated values?

How do I model Swift's enum cases with data in EK9?

How do I replace Swift associated values with EK9 composition?

Answer:
Swift enums can carry data per variant (associated values). EK9 enums are pure value types with no fields or methods. The EK9 equivalent uses enum + Dict for data, or enum + class hierarchy for complex variants.

SWIFT PATTERN: ASSOCIATED VALUES
Swift lets each enum case carry different data:
  enum Shape {
    case circle(radius: Double)
    case rectangle(width: Double, height: Double)
  }
Swift extracts data via pattern matching in switch.

EK9 PATTERN 1: ENUM + DICT FOR SIMPLE DATA
When each variant maps to a single value, use a Dict:
  defines type
    Colour: Red, Green, Blue
  labels <- {Colour.Red: "#FF0000", Colour.Green: "#00FF00", Colour.Blue: "#0000FF"}
This separates identity (enum) from data (Dict), keeping both independently testable and serialisable.

EK9 PATTERN 2: TRAIT HIERARCHY FOR COMPLEX VARIANTS
When variants carry different shaped data (like Swift's Shape example), use a trait with classes:
  defines trait
    Shape
      area() as pure abstract
        <- rtn as Float?
  defines class
    Circle with trait of Shape
      radius <- 0.0
      override area() as pure
        <- rtn as Float: 3.14159 * radius * radius
    Rectangle with trait of Shape
      ...
Each class carries its own data. The trait defines the shared contract.

WHY COMPOSITION IS SUPERIOR
Swift's associated values tightly couple identity and data. EK9's separation gives:
- Serialisability: enums convert to JSON automatically, associated values need custom Codable
- Testability: functions and Dict entries are independently testable
- Extensibility: add new data mappings without modifying the enum
- Quality: the compiler enforces metrics on each function independently

See Q100 for enum vs Java comparison. See Q219 for auto-generated operators. See Q225 for the composition pattern in detail. See Q106 for traits. See Q109 for composition over inheritance.

defines module qa.enums.swiftmigration

  defines type

    Colour
      Red
      Green
      Blue

    Shape
      Circle
      Rectangle
      Triangle

  defines trait

    <?-
      Shared contract for shapes with area calculation.
      This replaces Swift's enum with associated values.
    -?>
    Measurable
      area() as pure abstract
        <- rtn as Float?

  defines class

    CircleShape with trait of Measurable
      radius <- 0.0

      CircleShape()
        -> radius as Float
        this.radius: radius

      override area() as pure
        <- rtn as Float: 3.14159 * radius * radius

      default operator

    RectangleShape with trait of Measurable
      width <- 0.0
      height <- 0.0

      RectangleShape()
        ->
          width as Float
          height as Float
        this.width: width
        this.height: height

      override area() as pure
        <- rtn as Float: width * height

      default operator

  defines function

    <?-
      Maps shape type to a description string.
      This replaces Swift's associated value extraction.
    -?>
    describeShape() as pure
      -> shapeType as Shape
      <- description as String: switch shapeType
        <- rtn as String: String()
        case Shape.Circle
          rtn: "A round shape"
        case Shape.Rectangle
          rtn: "A four-sided shape"
        case Shape.Triangle
          rtn: "A three-sided shape"
        default
          rtn: "Unknown shape"

  defines program

    SwiftEnumMigrationDemo()
      stdout <- Stdout()

      // === PATTERN 1: ENUM + DICT FOR SIMPLE DATA ===

      hexCodes <- {Colour.Red: "#FF0000", Colour.Green: "#00FF00", Colour.Blue: "#0000FF"}

      for colour in Colour
        code <- hexCodes.getOrDefault(colour, "unknown")
        stdout.println(`${colour}: ${code}`)

      // === ENUM + FUNCTION FOR BEHAVIOUR ===

      for shape in Shape
        stdout.println(describeShape(shape))

      // === PATTERN 2: TRAIT HIERARCHY FOR COMPLEX VARIANTS ===

      circle <- CircleShape(5.0)
      rect <- RectangleShape(4.0, 6.0)

      stdout.println(`Circle area: ${circle.area()}`)
      stdout.println(`Rectangle area: ${rect.area()}`)

Swift: enum cases carry associated values (case circle(radius: Double)), extracted via pattern matching (case .circle(let r)), powerful but couples data to identity. Rust: enum variants with fields, extracted via match arms. Java: enum constants with fields and constructors. Kotlin: sealed class with data class variants. EK9: enums are pure values, use Dict for simple data mapping and trait + class hierarchy for complex variant data, separation of concerns.

associated composition data dict enum enumeration migrate pattern swift trait values variant

Explanation: String has no toUpperCase() method in EK9. Use upperCase() instead. See ek9 -h E50060 for details.

code <- hexCodes.getOrDefault(colour, "unknown").toUpperCase()


Question: How do I migrate Swift optional binding chains to EK9?

What is the EK9 equivalent of Swift if let?

How do I translate Swift guard let to EK9?

How do I replace Swift optional chaining in EK9?

What replaces Swift's force unwrap in EK9?

Answer:
Swift has five optional unwrapping patterns. EK9 replaces all of them with guard expressions and coalescing operators, with zero runtime crash risk.

SWIFT PATTERN 1: IF LET (SINGLE BINDING)
  Swift:  if let name = getName() { use(name) }
  EK9:    if name <- getName()
            use(name)
EK9's guard expression is the direct equivalent. The variable only exists inside the if block. Both skip the block when the value is absent or unset.

SWIFT PATTERN 2: IF LET CHAIN (MULTIPLE BINDINGS)
  Swift:  if let a = getA(), let b = getB() { use(a, b) }
  EK9:    if first <- getA()
            if second <- getB()
              use(first, second)
Swift chains multiple bindings in one if. EK9 uses nested guards. Each level adds a guard check.

SWIFT PATTERN 3: GUARD LET (EARLY EXIT)
  Swift:  guard let name = getName() else { return nil }
  EK9:    if name <- getName()
            processName(name)
EK9 has no return statement. Instead of guard-else-return, put the success path inside the if block. The else block (or simply falling through) handles the failure case.

SWIFT PATTERN 4: NIL COALESCING
  Swift:  let name = getName() ?? "default"
  EK9:    name <- getName() ?: "default"
Swift's ?? is a memory-level nil check. EK9's ?: checks isSet (more thorough). EK9 also has ?? for memory-level checks, plus <? and >? for coalescing comparisons.

SWIFT PATTERN 5: FORCE UNWRAP
  Swift:  let name = getName()!
  EK9:    (no equivalent: force unwrap does not exist)
EK9 has no force unwrap. There is no escape hatch. You must use a guard or coalescing operator. This eliminates the entire category of force-unwrap crashes.

SWIFT PATTERN 6: OPTIONAL CHAINING
  Swift:  let len = person?.address?.street?.count
  EK9:    if person <- getPerson()
            if addr <- person.address()
              streetLen <- length addr.street()
EK9 uses nested guards instead of ?. chaining. Each guard is explicit and checked by the compiler.

See Q47 for Optional basics. See Q74 for if guard patterns. See Q75 for switch guards. See Q79 for guarded assignment. See Q168 for fallback values. See Q243 for coalescing operators.

defines module qa.safeaccess.swiftoptional

  defines function

    <?-
      Simulates a lookup that might not find a value.
    -?>
    findUser() as pure
      -> userId as Integer
      <- rtn <- Optional() of String

      aliceId <- 1
      bobId <- 2

      if userId == aliceId
        rtn: Optional("Alice")
      else if userId == bobId
        rtn: Optional("Bob")

    <?-
      Simulates an address lookup that might not find a value.
    -?>
    findAddress() as pure
      -> name as String
      <- rtn <- Optional() of String

      if name == "Alice"
        rtn: Optional("123 Main Street")

  defines program

    SwiftOptionalMigrationDemo()
      stdout <- Stdout()

      // === PATTERN 1: IF LET (SINGLE BINDING) ===
      // Swift: if let user = findUser(1) { print(user) }

      if user <- findUser(1)
        stdout.println(`Found user: ${user.get()}`)

      // === PATTERN 2: IF LET CHAIN (NESTED GUARDS) ===
      // Swift: if let user = findUser(1), let addr = findAddress(user) { ... }

      if user <- findUser(1)
        if addr <- findAddress(user.get())
          stdout.println(`${user.get()} lives at ${addr.get()}`)

      // === PATTERN 3: GUARD LET BECOMES IF GUARD ===
      // Swift: guard let user = findUser(3) else { return }
      // EK9: success path inside the guard block

      if user <- findUser(3)
        stdout.println(`User 3: ${user.get()}`)
      else
        stdout.println("User 3 not found")

      // === PATTERN 4: NIL COALESCING ===
      // Swift: let name = findUser(99) ?? "Anonymous"

      lookup <- findUser(99)
      userName <- lookup.getOrDefault("Anonymous")
      stdout.println(`User 99: ${userName}`)

      // === NO FORCE UNWRAP ===
      // Swift: let name = findUser(1)!  (crashes if nil)
      // EK9: no equivalent. Must use guard or coalescing.

Swift: if let for binding, guard let for early exit, ?? for nil coalescing, ! for force unwrap (crash risk), ?. for optional chaining. Kotlin: ?.let{} for binding, ?: for elvis, !! for force unwrap (NPE risk). Java: Optional.map().orElse() chains, .get() throws NoSuchElementException. Rust: if let Some(v) for binding, unwrap() for panic, ? operator for propagation. EK9: guard expressions (if x <- expr()) for binding, ?: for isSet coalescing, ?? for memory coalescing, no force unwrap, nested guards for chaining.

binding chain coalescing force guard let migrate nil null-safe optional safe swift unwrap

Explanation: Optional has no unwrap() method. Use getOrDefault() for safe access with a fallback. EK9 has no force unwrap. See ek9 -h E50060 for details.

if user <- findUser(1)
        stdout.println(`Found user: ${user.getValue()}`)

Explanation: Optional has no getValue() method. Use get() inside a guard block. See ek9 -h E50060 for details.


Question: How do I migrate Swift control flow patterns to EK9?

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?

Answer:
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.

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}`)

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.

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

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

Explanation: EK9 has no return statement. Use if guard expressions for success paths. See ek9 -h E01072 for details.

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

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

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


Question: What is fuzzing and how does EK9 support it?

What is the difference between fuzzing, mutation testing, and test generation in EK9?

How does EK9 fuzz testing compare to AFL or libFuzzer?

Answer:
EK9 has three built-in fuzzing modes integrated directly into the compiler. No external tools, no separate configuration.

THREE MODES
1. Compiler fuzzing (-fuzz): generates random EK9 source files and feeds them through the compiler to find parser and type-checker crashes. This tests the compiler itself.
2. Mutation testing (-fuzzmutate): takes your source file, applies semantic mutations (swap operators, flip comparisons, remove guards), and checks whether your tests detect the change. Surviving mutants reveal weak tests.
3. Test generation (-fuzztest): analyses your source, harvests types and functions, generates edge-case test programs, compiles them to verify correctness, and outputs the survivors as @Test programs.

SOURCE-LEVEL AND TYPE-AWARE
Unlike AFL or libFuzzer which operate on raw bytes, EK9 fuzzing understands the grammar and type system. Generated programs are syntactically valid EK9. Mutations respect operator semantics. Test generation uses type information to choose meaningful edge-case values.

WHY BUILT-IN
Fuzzing as an external tool means most teams never use it. By integrating it into the compiler, EK9 makes fuzzing a normal part of the development workflow, not a specialist activity.

See Q750 for running the fuzzer. See Q753 for mutation testing. See Q754 for test generation. See Q758 for comparison with AFL and libFuzzer. See Q155 for writing tests. See Q157 for running tests.

defines module qa.fuzzingandmutation.overview

  <?-
    Code that benefits from all three fuzzing modes.
    The calculator operators would be flipped by mutation testing.
    The boundary checks would be probed by test generation.
    The parsing would be stressed by compiler fuzzing.
  -?>

  defines function

    safeDiv() as pure
      ->
        numerator as Float
        denominator as Float
      <- result as Float: 0.0

      zero <- 0.0
      if denominator <> zero
        result: numerator / denominator

    clamp() as pure
      ->
        low as Float
        high as Float
        input as Float
      <- result as Float: input

      if input < low
        result: low
      else if input > high
        result: high

  defines program

    FuzzingOverviewDemo()
      stdout <- Stdout()

      ten <- 10.0
      three <- 3.0
      stdout.println(`10 / 3 = ${safeDiv(ten, three)}`)

      zero <- 0.0
      stdout.println(`10 / 0 = ${safeDiv(ten, zero)}`)

      low <- 0.0
      high <- 100.0
      belowMin <- -5.0
      normal <- 50.0
      aboveMax <- 150.0
      stdout.println(`clamp(-5) = ${clamp(low, high, belowMin)}`)
      stdout.println(`clamp(50) = ${clamp(low, high, normal)}`)
      stdout.println(`clamp(150) = ${clamp(low, high, aboveMax)}`)

Java: no built-in fuzzer, use Jazzer (external, byte-level). Python: no built-in fuzzer, use Atheris or Hypothesis (external). Rust: cargo-fuzz wraps libFuzzer (external, byte-level). Go: go test -fuzz (built-in since 1.18, byte-level). JavaScript: no built-in, use jsfuzz (external). EK9: three built-in modes (compiler fuzz, mutation testing, test generation), all source-level and type-aware, integrated into the compiler.

AFL built-in compiler fuzz fuzzing generation libFuzzer mutation source-level test testing type-aware


Question: How do I run the EK9 fuzzer?

How do I fuzz the EK9 compiler to find crashes?

Answer:
The -fuzz flag runs grammar-based fuzz testing that stress-tests the compiler with randomly generated EK9 source files.

BASIC USAGE
  ek9 -fuzz 60
This runs the fuzzer for 60 minutes, generating random EK9 programs and feeding them through the full compilation pipeline. Crash-inducing inputs are saved to ./fuzz-crashes/ automatically.

DURATION
The parameter is in minutes. Use longer durations for deeper coverage:
  ek9 -fuzz 5      Quick smoke test (5 minutes).
  ek9 -fuzz 60     Normal development session (1 hour).
  ek9 -fuzz 1440   Overnight run (24 hours).

WORKER THREADS
The fuzzer uses multiple worker threads to maximise throughput. It automatically detects available CPU cores and distributes work across them.

WHAT IT GENERATES
Each iteration creates a syntactically-aware EK9 source file containing randomly selected constructs: functions, classes, records, traits, enumerations, streams, control flow, operators, and generics. The generator understands EK9 grammar rules so that most files parse successfully and exercise deeper compiler phases.

CRASH FILES
When the compiler throws an unexpected exception on a generated file, that file is saved to ./fuzz-crashes/ with a descriptive filename. These files are minimal reproduction cases for bug reports.

See Q749 for fuzzing overview. See Q751 for output formats. See Q752 for the HTML dashboard. See Q757 for crash file details. See Q2 for compile and run basics.

defines module qa.fuzzingandmutation.runfuzzer

  <?-
    Example code that a fuzzer might generate and test.
    This demonstrates the kind of constructs the grammar-based
    fuzzer combines: classes with operators, pure functions,
    stream pipelines, and control flow.
  -?>

  defines class

    Temperature
      reading as Float: Float()

      Temperature()
        ->
          initial as Float
        this.reading :=? initial

      operator $ as pure
        <- rtn as String: $this.reading

      operator < as pure
        -> other as Temperature
        <- rtn as Boolean: this.reading < other.reading

      default operator ?

  defines function

    averageReading()
      ->
        readings as List of Float
      <- average as Float: 0.0

      count <- length readings
      zero <- 0
      if count > zero
        total <- 0.0
        for reading in readings
          total: total + reading
        average: total / #^count

  defines program

    RunFuzzerDemo()
      stdout <- Stdout()

      readings <- [20.5, 22.1, 19.8, 25.3, 18.7]
      avg <- averageReading(readings)
      stdout.println(`Average temperature: ${avg}`)

      cold <- Temperature(15.0)
      warm <- Temperature(30.0)
      stdout.println(`Cold: ${cold}, Warm: ${warm}`)
      coldLessThanWarm <- cold < warm
      stdout.println(`Cold < Warm: ${coldLessThanWarm}`)

Java: no built-in fuzzer, use Jazzer or JQF (separate tools, separate setup). Python: no built-in, Hypothesis for property-based testing, Atheris for coverage-guided fuzzing. Rust: cargo-fuzz wraps libFuzzer (must write harness functions). Go: go test -fuzz (built-in since 1.18 but requires writing Fuzz* functions). EK9: ek9 -fuzz <minutes> runs immediately with zero setup, no harness code needed.

compiler crash duration fuzz generate grammar minutes random run smoke thread worker


Question: What fuzz output formats are available in EK9?

What is the difference between -fuzz, -fuzz0, -fuzz2, and -fuzz6?

How do I get JSON output from the EK9 fuzzer?

Answer:
EK9 fuzzing supports four output formats, using the same suffix pattern as the test runner (-t flags).

OUTPUT FORMAT FLAGS
  ek9 -fuzz <minutes>    Human-readable output (default, same as -fuzz1).
  ek9 -fuzz0 <minutes>   Terse output: one-line CI pass/fail summary.
  ek9 -fuzz2 <minutes>   JSON output: fuzz-report.json + fuzz-snapshots.jsonl.
  ek9 -fuzz6 <minutes>   HTML dashboard: fuzz-report/index.html.

HUMAN-READABLE (-fuzz)
Shows a rolling summary with iterations per second, crash count, phase distribution, and error code histogram. Best for interactive development.

TERSE (-fuzz0)
One-line output suitable for CI pipelines: total iterations, crashes found, duration. Exit code 0 if no crashes, non-zero otherwise.

JSON (-fuzz2)
Structured output for programmatic analysis:
  fuzz-report.json: final summary with crash count, iterations, timing.
  fuzz-snapshots.jsonl: periodic snapshots for throughput graphing.
Ideal for AI-assisted analysis or custom dashboards.

HTML DASHBOARD (-fuzz6)
Interactive report at fuzz-report/index.html with:
  Error code heatmap, phase distribution chart, throughput curve.
Same visual quality as the -t6 coverage dashboard.

CI INTEGRATION
For CI, use -fuzz0 with a short duration as a smoke test:
  ek9 -fuzz0 5
Check the exit code and fail the build if crashes are found.

See Q749 for fuzzing overview. See Q750 for running the fuzzer. See Q752 for reading the HTML dashboard. See Q157 for test output formats (same suffix pattern).

defines module qa.fuzzingandmutation.outputformats

  <?-
    Demonstrates code with multiple output paths that would
    produce different fuzz statistics depending on coverage.
    The output format flags control how those statistics
    are reported, not what the code does.
  -?>

  defines function

    categorise() as pure
      -> score as Integer
      <- category as String: "unknown"

      failThreshold <- 40
      passThreshold <- 70
      excellentThreshold <- 90

      if score < failThreshold
        category: "fail"
      else if score < passThreshold
        category: "pass"
      else if score < excellentThreshold
        category: "good"
      else
        category: "excellent"

    formatResult() as pure
      ->
        studentName as String
        score as Integer
      <- report as String: String()

      grade <- categorise(score)
      report: `${studentName}: ${score} (${grade})`

  defines program

    FuzzOutputFormatsDemo()
      stdout <- Stdout()

      scores <- [35, 55, 78, 95]
      names <- ["Alice", "Bob", "Carol", "Dave"]

      idx <- 0
      defaultName <- "Unknown"
      for score in scores
        studentName <- names.getOrDefault(idx, defaultName)
        if studentName?
          stdout.println(formatResult(studentName, score))
        idx: idx + 1

Java: Jazzer outputs to stdout or JUnit XML (limited formats). Python: Atheris writes crash files, no structured output. Rust: cargo-fuzz outputs crash files and stdout only. Go: go test -fuzz writes to testdata/ directory. EK9: four built-in output formats (human, terse, JSON, HTML) matching the test runner pattern, with CI-friendly exit codes.

CI dashboard format fuzz fuzz0 fuzz2 fuzz6 html json output report terse


Question: How do I read the EK9 fuzz HTML dashboard?

What does the fuzz-report/index.html show?

What are error code heatmaps in the fuzz report?

Answer:
The -fuzz6 flag generates an interactive HTML dashboard at fuzz-report/index.html with four key sections.

ERROR CODE HEATMAP
A colour-coded matrix of every compiler error code triggered during fuzzing. Brighter cells mean more frequent triggers. Error codes that never appear reveal gaps in the fuzzer's grammar coverage.

PHASE DISTRIBUTION
A bar chart showing how many generated files reached each compilation phase (PARSING, SYMBOL_DEFINITION, TYPE_HIERARCHY_CHECKS, PRE_IR_CHECKS, etc.). A healthy fuzzer produces files that exercise all phases, not just the parser.

THROUGHPUT CURVE
A time-series graph of iterations per second. Drops in throughput indicate complex generated files that take longer to compile. Sustained low throughput may reveal compiler performance issues.

CRASH SUMMARY
A table listing each unique crash with: error message, stack trace hash, affected phase, and link to the minimal reproduction file in ./fuzz-crashes/.

READING THE DASHBOARD
- All error codes triggered = good fuzzer coverage.
- Files reaching deep phases = grammar generator working well.
- Zero crashes after long runs = compiler is robust.
- Surviving error code gaps = improve generator for those constructs.

See Q749 for fuzzing overview. See Q751 for output formats. See Q627 for profiling dashboard (similar HTML pattern). See Q321 for quality report dashboard.

defines module qa.fuzzingandmutation.htmldashboard

  <?-
    Code with multiple error paths that produce
    a rich distribution in the fuzz heatmap.
    Different branches exercise different compiler
    phases and error detection logic.
  -?>

  defines trait

    Measurable
      measurement() as pure
        <- rtn as Float: 0.0

      label() as pure
        <- rtn as String: String()

  defines class

    Sensor as open
      sensorName as String: String()
      currentReading as Float: 0.0

      Sensor()
        ->
          initialName as String
        this.sensorName :=? initialName

      reading() as pure
        <- rtn as Float: this.currentReading

      updateReading()
        -> newReading as Float
        this.currentReading: newReading

      operator $ as pure
        <- rtn as String: `${this.sensorName}: ${this.currentReading}`

      default operator ?

  defines function

    summariseSensors()
      -> sensors as List of Sensor
      <- summary as String: "No sensors"

      count <- length sensors
      zero <- 0
      if count > zero
        total <- 0.0
        for sensor in sensors
          total: total + sensor.reading()
        avg <- total / #^count
        summary: `${count} sensors, avg ${avg}`

  defines program

    FuzzHtmlDashboardDemo()
      stdout <- Stdout()

      alpha <- Sensor("alpha")
      beta <- Sensor("beta")
      gamma <- Sensor("gamma")

      alpha.updateReading(23.5)
      beta.updateReading(19.8)
      gamma.updateReading(27.1)

      sensors <- [alpha, beta, gamma]
      for sensor in sensors
        stdout.println($sensor)

      stdout.println(summariseSensors(sensors))

Java: Jazzer provides basic crash reports, no HTML dashboard. Python: Atheris writes crash files only, visualisation requires custom tools. Rust: cargo-fuzz has no built-in dashboard. Go: go test -fuzz writes to testdata, no visualisation. EK9: built-in interactive HTML dashboard with heatmaps, charts, and crash details generated by -fuzz6.

code crash dashboard error fuzz heatmap html phase report statistics throughput visualisation


Question: How does mutation testing work in EK9?

How do I assess test quality with mutation testing in EK9?

What mutation categories does EK9 apply?

Answer:
Mutation testing measures the quality of your tests by introducing small, deliberate bugs into your source code and checking whether your tests detect them.

BASIC USAGE
  ek9 -fuzzmutate source.ek9 mutations/
This analyses source.ek9, generates mutated variants, and writes them to the mutations/ directory. Each variant has exactly one semantic change that compiles but alters behaviour.

MUTATION CATEGORIES
The mutator applies these categories of change:
  ARITH_SWAP: + becomes -, * becomes / (and vice versa).
  CMP_FLIP: < becomes >=, == becomes <>.
  GUARD_STRIP: removes if-guard conditions (if x <- expr becomes unconditional).
  CONST_BUMP: changes literal values (0 becomes 1, boundary shifts).
  NEGATE_BOOL: flips Boolean expressions (true becomes false).
  SWAP_LINES: reorders independent statements.
  DUP_OPERAND: duplicates an operand (x + y becomes x + x).
  REMOVE_CALL: removes a method call statement.

MANIFEST FILE
The mutations/ directory contains a manifest.txt listing each variant with its mutation category, location, and description. This file is machine-readable for CI integration.

KILL CHECK
Run your tests against each mutant to check detection:
  ek9 -fuzzmutate source.ek9 mutations/ -test tests.ek9
Mutants that survive (tests still pass) reveal gaps in your test suite.

OPTIONS
  -n 50          Generate at most 50 mutants (default 100).
  -seed 42       Reproducible mutation selection.
  -overwrite     Replace existing mutations/ directory.

See Q749 for fuzzing overview. See Q754 for test generation. See Q756 for the quality loop. See Q155 for writing tests. See Q206 for test coverage.

defines module qa.fuzzingandmutation.mutationtesting

  <?-
    Code designed to show how mutation testing reveals weak tests.
    Each operator and comparison is a mutation target.
    If tests do not check boundary conditions, mutants survive.
  -?>

  defines function

    <?-
      A mutation tester would flip < to >=, changing the behaviour.
      If your test only checks the happy path, the mutant survives.
    -?>
    isWithinRange() as pure
      ->
        low as Integer
        high as Integer
        candidate as Integer
      <- inRange as Boolean: false

      if candidate >= low and candidate <= high
        inRange: true

    <?-
      ARITH_SWAP would change + to - in the discount calculation.
      CMP_FLIP would change > to <= in the threshold check.
    -?>
    applyDiscount() as pure
      ->
        price as Float
        discountRate as Float
      <- finalPrice as Float: price

      minimumRate <- 0.0
      maximumRate <- 1.0
      if discountRate > minimumRate and discountRate <= maximumRate
        reduction <- price * discountRate
        finalPrice: price - reduction

  defines program

    MutationTestingDemo()
      stdout <- Stdout()

      low <- 1
      high <- 10
      for candidate in [0, 1, 5, 10, 11]
        result <- isWithinRange(low, high, candidate)
        stdout.println(`${candidate} in [${low}..${high}]: ${result}`)

      price <- 100.0
      rate <- 0.25
      discounted <- applyDiscount(price, rate)
      stdout.println(`Price ${price} at ${rate} discount: ${discounted}`)

Java: PIT (Pitest) for mutation testing, separate Maven/Gradle plugin, slow on large codebases. Python: mutmut or cosmic-ray (external tools). Rust: no mainstream mutation testing tool. Go: no built-in, experimental tools like go-mutesting. EK9: built-in ek9 -fuzzmutate with manifest output, kill-check via -test flag, and reproducible seeds.

flip fuzzmutate guard kill mutant mutate mutation quality strip survive swap test testing


Question: How do I generate test cases with -fuzztest in EK9?

How does EK9 automatically generate unit tests?

How do I use EK9 test generation for edge cases?

Answer:
The -fuzztest flag analyses your source code and automatically generates edge-case @Test programs.

BASIC USAGE
  ek9 -fuzztest source.ek9 output.ek9
This reads source.ek9, harvests all functions and types, generates candidate test programs, compiles each to verify correctness, and writes the survivors to output.ek9.

SYMBOL HARVESTING
The generator inspects your source to discover:
  Functions: parameter types, return types, and purity.
  Classes: constructors, operators, and public methods.
  Records: constructors and operators.
  Enumerations: values and constrained ranges.

EDGE-CASE VALUES
For each parameter type, the generator selects meaningful edge-case values:
  Integer: 0, 1, -1, MAX, MIN, boundary values.
  Float: 0.0, -0.0, very small, very large, boundary values.
  String: empty, single char, very long, special characters.
  Boolean: true, false.
  Collections: empty, single item, many items.

COMPILE-CHECK-DISCARD
Each generated test candidate is compiled through PRE_IR_CHECKS. If it fails to compile (type error, missing operator, invalid combination), it is silently discarded. Only compilable, type-correct tests survive into the output file.

OPTIONS
  -n 200          Generate at most 200 candidates (default 200).
  -seed 42        Reproducible test generation.
  -overwrite      Replace existing output file.

OUTPUT FORMAT
The output file is a compilable EK9 module containing @Test programs. Each test calls one function or method with edge-case arguments and prints the result for manual review.

See Q749 for fuzzing overview. See Q753 for mutation testing. See Q755 for compile-check-discard philosophy. See Q155 for writing tests. See Q157 for running tests.

defines module qa.fuzzingandmutation.generatetests

  <?-
    Source code that would be analysed by -fuzztest.
    The generator would harvest these functions and create
    @Test programs exercising edge-case inputs.
  -?>

  defines function

    <?-
      Test generator would try: 0, 1, -1, MAX, MIN.
      Edge case: negative numbers should return false.
    -?>
    isPrime()
      -> candidate as Integer
      <- prime as Boolean: false

      two <- 2
      if candidate >= two
        divisor <- two
        foundDivisor <- false
        while divisor * divisor <= candidate and not foundDivisor
          zero <- 0
          if candidate mod divisor == zero
            foundDivisor: true
          divisor: divisor + 1
        prime: not foundDivisor

    <?-
      Test generator would try: empty string, single char,
      already uppercase, mixed case.
    -?>
    isAllUpperCase()
      -> text as String
      <- allUpper as Boolean: false

      if text?
        upper <- text.upperCase()
        allUpper: text == upper

  defines program

    GenerateTestsDemo()
      stdout <- Stdout()

      for candidate in [1, 2, 3, 4, 5, 17, 20, 97]
        stdout.println(`${candidate} prime: ${isPrime(candidate)}`)

      for word in ["HELLO", "Hello", "world", "EK9"]
        stdout.println(`${word} all upper: ${isAllUpperCase(word)}`)

Java: no built-in test generation, use EvoSuite or Randoop (external, bytecode-level). Python: Hypothesis generates property-based tests (requires writing strategies). Rust: proptest for property-based testing (requires writing arbitraries). Go: go test -fuzz generates inputs but not full test functions. EK9: ek9 -fuzztest generates complete @Test programs from source analysis, compile-checked for correctness.

boundary candidate case check compile discard edge fuzztest generate generation harvest symbol test


Question: Why does EK9 use compile-check-discard for generated tests?

How does EK9 ensure generated tests are valid?

What is the compile-check-discard pattern in EK9 test generation?

Why does EK9 compile generated tests before outputting them?

Answer:
EK9 test generation uses a compile-check-discard pattern: generate a candidate test, compile it, keep it only if compilation succeeds. This is fundamentally different from every other test generation tool.

THE COMPILER IS THE FILTER
Other tools generate tests and hope they are valid. EK9 generates candidates and uses its own compiler as an oracle. A candidate that fails type checking, violates purity, uses an invalid operator, or triggers any quality check is silently discarded. Only programs that pass ALL compiler checks survive.

WHY THIS WORKS IN EK9
EK9 has no warnings. Every check is pass or fail. This means the compiler gives a definitive answer: this candidate is either fully valid or it is not. There is no ambiguity, no 'valid with warnings' gray area.

TYPE-AWARE GENERATION
Because the generator knows the type system, it generates calls with correct types. But edge-case combinations (empty string to a function expecting non-empty, zero to a divider) may fail purity or data-flow checks. Compile-check-discard handles this automatically.

PRACTICAL BENEFIT
The output file contains ONLY tests that:
  1. Parse correctly (valid EK9 syntax).
  2. Pass type checking (correct argument types).
  3. Pass data flow analysis (no uninitialised variables).
  4. Pass quality checks (no magic literals, no dead code).
This is a guarantee no other test generator provides.

NO OTHER LANGUAGE DOES THIS
AFL, libFuzzer, EvoSuite, Hypothesis, and cargo-fuzz all generate inputs, not full programs. EK9 generates complete @Test programs and validates them against the full compiler pipeline.

See Q749 for fuzzing overview. See Q754 for test generation. See Q753 for mutation testing. See Q310 for quality at compile time.

defines module qa.fuzzingandmutation.compilecheckdiscard

  <?-
    Demonstrates functions whose edge-case combinations
    could fail quality checks. The compile-check-discard
    pattern automatically handles these cases.
  -?>

  defines function

    <?-
      Test generator might try dividing by zero.
      The guard prevents a crash, but a candidate test
      calling this with zero would still compile because
      the guard handles it. Compile-check-discard keeps it.
    -?>
    safeDivide() as pure
      ->
        numerator as Integer
        divisor as Integer
      <- result as Integer: 0

      zero <- 0
      if divisor <> zero
        result: numerator / divisor

    <?-
      Test generator might try empty strings.
      The guard prevents accessing an empty string.
      Generated tests with empty input compile and exercise the guard.
    -?>
    safeFirstChar() as pure
      -> text as String
      <- found as Character: Character()

      if text?
        found :=? text.first()

    validateAge() as pure
      -> age as Integer
      <- valid as Boolean: false

      minimumAge <- 0
      maximumAge <- 150
      if age >= minimumAge and age <= maximumAge
        valid: true

  defines program

    CompileCheckDiscardDemo()
      stdout <- Stdout()

      ten <- 10
      three <- 3
      zero <- 0
      stdout.println(`10 / 3 = ${safeDivide(ten, three)}`)
      stdout.println(`10 / 0 = ${safeDivide(ten, zero)}`)

      greeting <- "Hello"
      emptyText <- String()
      stdout.println(`first of Hello: ${safeFirstChar(greeting)}`)
      stdout.println(`first of empty: ${safeFirstChar(emptyText)}`)

      for age in [0, 25, 150, -1, 200]
        stdout.println(`age ${age} valid: ${validateAge(age)}`)

Java: EvoSuite generates tests that may not compile or may have warnings. Python: Hypothesis generates inputs, not full test programs. Rust: proptest generates values, not complete test functions. Go: go test -fuzz generates corpus entries, not test functions. EK9: generates complete @Test programs validated by the full compiler pipeline, discarding any that fail any check.

aware check compile discard filter generation guarantee oracle quality test type valid


Question: How does fuzzing relate to code coverage and quality in EK9?

What is the EK9 quality loop for testing?

How do I combine fuzzing, mutation testing, and coverage in EK9?

How do I improve test quality in EK9 systematically?

Answer:
EK9 provides a complete quality loop: write tests, measure coverage, generate more tests, mutate to assess quality, and fuzz for robustness.

THE QUALITY LOOP (5 STEPS)
1. Write tests: create @Test programs for your functions.
  ek9 -t source.ek9
2. Check coverage: identify untested code.
  ek9 -tC source.ek9
  Coverage must reach 80% to package with -P.
3. Generate tests: fill coverage gaps automatically.
  ek9 -fuzztest source.ek9 generated.ek9
  Review generated tests, keep the useful ones.
4. Mutation testing: assess test quality.
  ek9 -fuzzmutate source.ek9 mutations/ -test tests.ek9
  Surviving mutants show where tests are too weak.
5. Compiler fuzzing: verify robustness.
  ek9 -fuzz 60
  Ensures the compiler handles edge cases in your patterns.

COVERAGE AND MUTATION TOGETHER
Coverage tells you what code is executed by tests. Mutation testing tells you whether tests actually verify behaviour. 100% coverage with weak assertions is meaningless. Mutation testing reveals this gap.

AI-ASSISTED WORKFLOW
Use -t2 (JSON test output) and -fuzz2 (JSON fuzz output) to feed results into AI analysis. The AI can identify patterns in surviving mutants and suggest targeted tests.

CONTINUOUS IMPROVEMENT
Run the quality loop regularly:
  Development: write tests, check coverage.
  Pre-commit: mutation testing on changed files.
  CI: short fuzz run as smoke test.
  Nightly: long fuzz run for deep coverage.

See Q749 for fuzzing overview. See Q753 for mutation testing. See Q754 for test generation. See Q155 for writing tests. See Q206 for test coverage. See Q310 for quality at compile time.

defines module qa.fuzzingandmutation.qualityloop

  <?-
    A small library with tests that demonstrate the quality loop.
    Step 1: Write tests. Step 2: Check coverage.
    Step 3: Generate more. Step 4: Mutate. Step 5: Fuzz.
  -?>

  defines function

    maximum() as pure
      ->
        first as Integer
        second as Integer
      <- result as Integer: first

      if second > first
        result: second

    minimum() as pure
      ->
        first as Integer
        second as Integer
      <- result as Integer: first

      if second < first
        result: second

    absoluteValue() as pure
      -> number as Integer
      <- result as Integer: number

      zero <- 0
      if number < zero
        result: 0 - number

  defines program

    // === STEP 1: Write basic tests ===

    @Test
    MaximumTest()
      three <- 3
      seven <- 7
      assert maximum(three, seven) == 7
      assert maximum(seven, three) == 7

    @Test
    MinimumTest()
      three <- 3
      seven <- 7
      assert minimum(three, seven) == 3
      assert minimum(seven, three) == 3

    @Test
    AbsoluteValueTest()
      five <- 5
      negativeFive <- -5
      zero <- 0
      assert absoluteValue(five) == 5
      assert absoluteValue(negativeFive) == 5
      assert absoluteValue(zero) == 0

    // === Demo program showing the functions ===

    QualityLoopDemo()
      stdout <- Stdout()

      three <- 3
      seven <- 7
      stdout.println(`max(3, 7) = ${maximum(three, seven)}`)
      stdout.println(`min(3, 7) = ${minimum(three, seven)}`)
      stdout.println(`abs(-5) = ${absoluteValue(-5)}`)

Java: requires separate tools for each step (JUnit, JaCoCo, PIT, Jazzer) with different configs. Python: pytest + pytest-cov + mutmut + Atheris (all separate installs). Rust: cargo-test + tarpaulin + cargo-fuzz (no mutation tool). Go: go test + go test -cover + go test -fuzz (no mutation tool). EK9: complete quality loop in one tool: -t for tests, -tC for coverage, -fuzztest for generation, -fuzzmutate for mutation, -fuzz for robustness.

CI continuous coverage fuzz generation improve loop mutation quality systematic test workflow


Question: Where do crash files and generated tests go in EK9?

How do I manage EK9 fuzzer output files?

What options control EK9 fuzz output locations?

Answer:
EK9 fuzzing tools write output to predictable locations with options to control overwriting and reproducibility.

CRASH FILES (-fuzz)
Location: ./fuzz-crashes/
When the compiler crashes on a generated file, the minimal reproduction case is saved here. Each file has a descriptive name including the phase that crashed and a hash of the stack trace.

GENERATED TESTS (-fuzztest)
  ek9 -fuzztest source.ek9 output.ek9
The second argument is the output file path. By default, the command errors if the output file already exists. Use -overwrite to replace it.

MUTATION VARIANTS (-fuzzmutate)
  ek9 -fuzzmutate source.ek9 mutations/
The second argument is the output directory. Contains:
  manifest.txt: lists each variant with mutation category and location.
  variant_001.ek9, variant_002.ek9, ...: individual mutated files.
By default, errors if the directory already exists. Use -overwrite to replace.

CONTROLLING OUTPUT
  -overwrite     Allow replacing existing output file or directory.
  -seed <n>      Reproducible generation (same seed produces same output).
  -n <n>         Maximum number of candidates (-fuzztest default 200, -fuzzmutate default 100).

CI WORKFLOW
In CI, use -overwrite so that each build produces fresh output:
  ek9 -fuzztest source.ek9 generated-tests.ek9 -overwrite
  ek9 -fuzzmutate source.ek9 mutations/ -overwrite -test tests.ek9

See Q749 for fuzzing overview. See Q750 for running the fuzzer. See Q753 for mutation testing. See Q754 for test generation. See Q2 for compile and run.

defines module qa.fuzzingandmutation.crashfiles

  <?-
    Demonstrates code patterns that might produce crash
    files when fuzzed, and test output when generated.
    The output management options ensure CI repeatability.
  -?>

  defines class

    Stack
      items as List of String: List() of String

      push()
        -> item as String
        this.items += item

      pop()
        <- popped as String: String()

        count <- length this.items
        zero <- 0
        emptyDefault <- String()
        if count > zero
          lastIndex <- count - 1
          topItem <- this.items.getOrDefault(lastIndex, emptyDefault)
          if topItem?
            popped: topItem
            this.items -= topItem

      peek() as pure
        <- topItem as String: String()

        count <- length this.items
        zero <- 0
        emptyDefault <- String()
        if count > zero
          lastIndex <- count - 1
          topItem :=? this.items.getOrDefault(lastIndex, emptyDefault)

      size() as pure
        <- count as Integer: length this.items

      operator $ as pure
        <- rtn as String: $this.items

      default operator ?

  defines program

    CrashFilesDemo()
      stdout <- Stdout()

      stack <- Stack()
      stack.push("first")
      stack.push("second")
      stack.push("third")
      stdout.println(`Stack: ${stack}`)

      popped <- stack.pop()
      stdout.println(`Popped: ${popped}`)
      stdout.println(`Peek: ${stack.peek()}`)
      stdout.println(`Size: ${stack.size()}`)

Java: Jazzer saves crashes to a corpus directory, no structured manifest. Python: Atheris writes to crash-* files in current directory. Rust: cargo-fuzz saves to fuzz/artifacts/<target>/. Go: go test -fuzz saves to testdata/fuzz/<FuzzTestName>/. EK9: crash files in ./fuzz-crashes/, generated tests to specified file, mutations to specified directory with manifest.txt, all controllable via -overwrite, -seed, -n.

crash directory files fuzz-crashes location manifest mutation output overwrite reproducible seed variant


Question: How is EK9 fuzzing different from AFL or libFuzzer?

Why is EK9 fuzzing source-level instead of byte-level?

How does EK9 fuzzing compare to coverage-guided fuzzing?

What advantages does type-aware fuzzing give EK9?

Answer:
EK9 fuzzing differs from traditional fuzzers in three fundamental ways: it operates at source level, understands types, and provides three modes instead of one.

SOURCE-LEVEL VS BYTE-LEVEL
AFL and libFuzzer mutate raw bytes and feed them to a compiled binary. Most mutated inputs are immediately rejected as invalid. EK9 generates valid source programs from the grammar, so every generated file is syntactically correct and exercises deeper compiler phases.

TYPE-AWARE VS TYPE-BLIND
AFL has no concept of types. It might flip a bit in a pointer, producing a segfault rather than testing logic. EK9 understands Integer, Float, String, and custom types. Generated programs use correct types, operators, and calling conventions.

THREE MODES VS ONE
AFL does one thing: coverage-guided binary fuzzing. EK9 provides:
  1. Compiler fuzzing (-fuzz): stress-test the compiler itself.
  2. Mutation testing (-fuzzmutate): assess test quality.
  3. Test generation (-fuzztest): create edge-case tests.
These three modes cover different quality dimensions.

NO HARNESS REQUIRED
AFL and libFuzzer require writing a harness function that accepts byte input and converts it to the target format. EK9 fuzzing works on source files directly with zero setup.

COVERAGE-GUIDED VS GRAMMAR-GUIDED
AFL uses code coverage feedback to guide mutations toward new execution paths. EK9 uses grammar rules and type information to generate structurally diverse programs. Both approaches find different kinds of bugs.

WHEN TO USE WHICH
  EK9 fuzzing: for EK9 projects, always use the built-in tools.
  AFL/libFuzzer: for C/C++ libraries or when testing binary interfaces.
  Both: complementary approaches for different layers of the stack.

See Q749 for fuzzing overview. See Q750 for running the fuzzer. See Q753 for mutation testing. See Q754 for test generation.

defines module qa.fuzzingandmutation.fuzzvsafl

  <?-
    Demonstrates type-aware code that a byte-level fuzzer
    would struggle with but EK9 source-level fuzzing handles
    naturally. The type system ensures generated inputs are meaningful.
  -?>

  defines type

    Priority
      Low
      Normal
      High
      Critical

  defines class

    Task
      taskName as String: String()
      taskPriority as Priority: Priority()
      completed as Boolean: false

      Task()
        ->
          initialName as String
          initialPriority as Priority
        this.taskName :=? initialName
        this.taskPriority :=? initialPriority

      complete()
        this.completed: true

      isComplete() as pure
        <- done as Boolean: this.completed

      priority() as pure
        <- rtn as Priority: this.taskPriority

      operator $ as pure
        <- rtn as String: `${this.taskName} [${this.taskPriority}]`

      default operator ?

  defines function

    countByPriority() as pure
      ->
        tasks as List of Task
        targetPriority as Priority
      <- count as Integer: 0

      for task in tasks
        if task.priority() == targetPriority
          count: count + 1

  defines program

    FuzzVsAflDemo()
      stdout <- Stdout()

      tasks <- List() of Task
      tasks += Task("Write docs", Priority.Low)
      tasks += Task("Fix login", Priority.Critical)
      tasks += Task("Add tests", Priority.High)
      tasks += Task("Update deps", Priority.Normal)
      tasks += Task("Review PR", Priority.High)

      for task in tasks
        stdout.println($task)

      highCount <- countByPriority(tasks, Priority.High)
      criticalCount <- countByPriority(tasks, Priority.Critical)
      stdout.println(`High priority: ${highCount}`)
      stdout.println(`Critical: ${criticalCount}`)

Java: Jazzer (coverage-guided, byte-level, requires harness). Python: Atheris (coverage-guided via libFuzzer, byte-level, requires harness). Rust: cargo-fuzz (coverage-guided via libFuzzer, byte-level, requires harness). Go: go test -fuzz (coverage-guided, byte-level, requires Fuzz* functions). AFL/libFuzzer: coverage-guided, byte-level, external tools. EK9: grammar-guided, source-level, type-aware, three modes, zero harness, built-in.

AFL byte-level comparison coverage grammar guided harness libFuzzer modes source-level three type-aware