Question: How do I implement the factory pattern in EK9?

How do I create objects via a factory function in EK9?

How do I return different implementations from a function?

How do I use switch to select implementations in EK9?

Answer:
EK9 implements the factory pattern using functions that return trait implementations. The factory uses switch/case to select the appropriate concrete class based on input parameters.

TRAIT AS PRODUCT INTERFACE
Define a trait as the product contract:
  Logger
    log() as abstract
      -> msg as String
      <- rtn as String?
All factory products implement this trait.

CONCRETE IMPLEMENTATIONS
Create classes that implement the trait:
  ConsoleLogger with trait of Logger
    override log()
      -> msg as String
      <- rtn as String: "[CONSOLE] " + msg
  PrefixLogger with trait of Logger
    override log()
      -> msg as String
      <- rtn as String: "[PREFIX] " + msg

FACTORY FUNCTION WITH SWITCH
The factory selects the implementation:
  createLogger()
    -> logType as String
    <- rtn as Logger?
    switch logType
      case "console"
        rtn: ConsoleLogger()
      default
        rtn: ConsoleLogger()

DYNAMIC CLASS FACTORY
For lightweight one-off implementations, create dynamic classes inside the factory:
  customLogger <- () with trait of Logger as class
    override log()
      -> msg as String
      <- rtn as String: "[CUSTOM] " + msg

WHEN TO USE FACTORY
Use factories when the caller should not know which concrete class is created. The factory encapsulates the creation logic and returns the trait type.

See Q103 for abstract classes as alternatives to traits. See Q115 for dynamic classes in factories. See Q211 for dispatcher as an alternative to factory switch logic.

defines module qa.patterns.factory

  defines trait

    Logger
      log() as abstract
        -> msg as String
        <- rtn as String?

  defines class

    ConsoleLogger with trait of Logger
      override log()
        -> msg as String
        <- rtn as String: "[CONSOLE] " + msg

      default operator ?

    PrefixLogger with trait of Logger
      logPrefix as String: "[INFO]"

      PrefixLogger()
        -> p as String
        this.logPrefix: p

      override log()
        -> msg as String
        <- rtn as String: `${logPrefix} ${msg}`

      default operator ?

  defines function

    createLogger()
      -> logType as String
      <- rtn as Logger?
      switch logType
        case "console"
          rtn: ConsoleLogger()
        case "prefix"
          rtn: PrefixLogger("[WARN]")
        default
          rtn: ConsoleLogger()

  defines program

    FactoryPatternDemo()
      stdout <- Stdout()

      // === FACTORY FUNCTION ===

      console <- createLogger("console")
      stdout.println(console.log("Hello from factory"))

      prefixed <- createLogger("prefix")
      stdout.println(prefixed.log("Warning message"))

      fallback <- createLogger("unknown")
      stdout.println(fallback.log("Fallback to default"))

      // === DYNAMIC CLASS AS PRODUCT ===

      custom <- () with trait of Logger as class
        override log()
          -> msg as String
          <- rtn as String: "[CUSTOM] " + msg

      stdout.println(custom.log("Dynamic logger"))

Java: Factory Method pattern with abstract creator class, Abstract Factory for families, static factory methods. Python: factory functions, __init__ with type dispatch. Rust: associated functions (Type::new()), builder pattern. Go: constructor functions NewType(), no inheritance. Kotlin: companion object factory methods, sealed classes with when. EK9: factory functions returning trait types, switch/case for selection, dynamic classes for inline implementations.

builder concrete construct create design factory idiom implementation pattern select switch trait

Explanation: When implementing an abstract trait method in a class, the 'override' keyword is required. ConsoleLogger and PrefixLogger must use 'override log()' to implement Logger's abstract method. See ek9 -h E05120 for details.


Question: How do I implement the adapter pattern in EK9?

How do I adapt one interface to another in EK9?

How do I wrap an existing class with a new interface?

How do I translate between incompatible interfaces in EK9?

Answer:
EK9 implements the adapter pattern using composition with trait implementation. The adapter class wraps the adaptee as a field and translates method calls from the new interface to the old one.

ADAPTER STRUCTURE
The pattern has three parts:
  1. Adaptee: the existing class with its own interface
  2. Target trait: the new interface clients expect
  3. Adapter: wraps the adaptee and implements the target trait

COMPOSITION WRAPPING
The adapter holds the adaptee as a private field:
  PrinterAdapter with trait of ModernOutput
    printer as OldPrinter
The adapter translates ModernOutput.render() into OldPrinter.printText().

METHOD TRANSLATION
The adapter overrides trait methods and delegates to the adaptee:
  override render()
    -> content as String
    <- rtn as String: printer.printText(content)
Clients call render() but the work is done by printText().

TRAIT DELEGATION SHORTCUT
When the adaptee already partially matches the target trait, use trait delegation with by to auto-forward compatible methods and only override the incompatible ones.

WHEN TO USE ADAPTER
Use when integrating code with different interfaces. The adapter translates without modifying either the adaptee or the client code.

See Q109 for composition patterns. See Q210 for trait delegation with by. See Q212 for composition over inheritance.

defines module qa.patterns.adapter

  defines class

    // The existing class with its own interface
    OldPrinter

      printText()
        -> text as String
        <- rtn as String: "[OLD] " + text

      default operator ?

  defines trait

    // The new interface clients expect
    ModernOutput
      render() as abstract
        -> content as String
        <- rtn as String?

  defines class

    // Adapter: wraps OldPrinter, implements ModernOutput
    PrinterAdapter with trait of ModernOutput
      printer as OldPrinter: OldPrinter()

      PrinterAdapter()
        -> p as OldPrinter
        this.printer: p

      override render()
        -> content as String
        <- rtn as String: printer.printText(content)

      default operator ?

  defines program

    AdapterPatternDemo()
      stdout <- Stdout()

      // === OLD INTERFACE ===

      oldPrinter <- OldPrinter()
      stdout.println(oldPrinter.printText("Direct call"))

      // === ADAPTED INTERFACE ===

      adapter <- PrinterAdapter(oldPrinter)
      stdout.println(adapter.render("Through adapter"))

      // === CLIENT USES MODERN INTERFACE ===
      // The client only knows about ModernOutput

      outputs <- List() of ModernOutput
      outputs += PrinterAdapter(OldPrinter())
      outputs += PrinterAdapter(OldPrinter())

      for output in outputs
        stdout.println(output.render("Polymorphic call"))

Java: Adapter pattern with class wrapping or interface delegation, anonymous classes. Python: duck typing often avoids adapter, __getattr__ for delegation. Rust: newtype pattern (struct Wrapper(Inner)), impl Trait for Wrapper. Go: struct embedding, interface satisfaction. Kotlin: class delegation with by keyword. EK9: composition wrapping with trait implementation, trait delegation with by for partial adaptation.

adapter bridge compatibility compose convert delegate design idiom interface pattern translate wrap

Explanation: When implementing an abstract trait method in a class, the 'override' keyword is required. PrinterAdapter must use 'override render()' to implement ModernOutput's abstract render method. See ek9 -h E05120 for details.

Explanation: OldPrinter is closed by default and cannot be extended. EK9 types are closed unless declared 'as open'. Use composition (wrapping as a field) and trait implementation instead of inheritance. See ek9 -h E05030 for details.


Question: How do I implement a fluent API in EK9?

How do I create a builder with method chaining?

What is the EK9 approach to fluent interfaces?

Answer:
EK9 supports fluent APIs through two approaches: immutable builder classes where each method returns a new instance, and stream pipelines which are the natural fluent pattern.

IMMUTABLE BUILDER PATTERN
Each method returns a new builder instance with accumulated state:
  QueryBuilder
    query as String
    select()
      -> columns as String
      <- rtn as QueryBuilder: QueryBuilder("SELECT " + columns)
    from()
      -> table as String
      <- rtn as QueryBuilder: QueryBuilder(query + " FROM " + table)
This is immutable: each call creates a new builder rather than mutating the current one.

CHAINING CALLS
Store intermediate results and chain:
  qb <- QueryBuilder()
  qb: qb.select("name, age")
  qb: qb.from("users")
  qb: qb.where("age > 18")
  stdout.println(qb.build())

STREAM PIPELINES AS FLUENT API
EK9's stream pipelines are the idiomatic fluent pattern:
  cat numbers | filter by isEven | map with doubleIt | collect as List of Integer
This is naturally fluent: each stage passes results to the next.

WHY NOT RETURN THIS
In many languages, fluent APIs work by returning 'this' from each method. EK9 encourages immutability: methods return new instances rather than mutating and returning the same object. This makes each intermediate state independent and safe to reuse.

WHEN TO USE EACH
Use immutable builders for configuration and query construction. Use stream pipelines for data transformation. Use function composition (Q59) for processing chains.

See Q89 for list streams. See Q235 for stream operations reference. See Q237 for streams vs loops.

defines module qa.patterns.fluent

  defines function

    isEven() as pure
      -> num as Integer
      <- rtn as Boolean: num mod 2 == 0

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

  defines class

    QueryBuilder
      query as String: String()

      default QueryBuilder()

      QueryBuilder()
        -> q as String
        this.query :=? String(q)

      select()
        -> columns as String
        <- rtn as QueryBuilder: QueryBuilder("SELECT " + columns)

      from()
        -> table as String
        <- rtn as QueryBuilder: QueryBuilder(`${query} FROM ${table}`)

      where()
        -> condition as String
        <- rtn as QueryBuilder: QueryBuilder(`${query} WHERE ${condition}`)

      build() as pure
        <- rtn as String: String(query)

      default operator ?

  defines program

    FluentApiDemo()
      stdout <- Stdout()

      // === IMMUTABLE BUILDER ===

      qb <- QueryBuilder()
      qb: qb.select("name, age")
      qb: qb.from("users")
      qb: qb.where("age > 18")
      stdout.println(qb.build())

      // === REUSABLE BASE ===

      base <- QueryBuilder().select("id, name")
      fromUsers <- base.from("users")
      fromOrders <- base.from("orders")
      stdout.println(fromUsers.build())
      stdout.println(fromOrders.build())

      // === STREAM PIPELINE: NATURAL FLUENT ===

      numbers <- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
      evens <- cat numbers | filter by isEven | map with doubleIt | collect as List of Integer
      stdout.println(`Doubled evens: ${evens}`)

Java: fluent APIs return this, StringBuilder pattern, Stream API for data. Python: method chaining returns self, list comprehensions. Rust: builder pattern with consume-and-return, iterator chains. Go: functional options pattern, no method chaining convention. Kotlin: apply/also scope functions, Sequence for lazy chains. EK9: immutable builder (each method returns new instance), stream pipelines (cat | filter | map | collect) as natural fluent API.

api builder chain compose design fluent idiom immutable method pattern pipeline stream

Explanation: Class fields must be initialised inline. Declaring a field without an initialiser triggers E08180 — uninitialised field. Always provide a default value. See ek9 -h E08180 for details.

Explanation: Renaming the variable means later references to 'qb' become unresolved, triggering E50001. Variable names must be consistent throughout the scope. See ek9 -h E50001 for details.


Question: How do I handle cross-cutting concerns in EK9?

How do I add logging without modifying existing code?

How do I implement the decorator pattern in EK9?

How do I stack multiple behaviors in EK9?

Answer:
EK9 handles cross-cutting concerns using the decorator pattern via trait delegation. Each decorator wraps a service, adds behavior before or after, then delegates to the wrapped service.

SERVICE TRAIT
Define the service contract as a trait:
  Service
    execute() as abstract
      -> request as String
      <- rtn as String?

CORE IMPLEMENTATION
The base service handles the actual work:
  CoreService with trait of Service
    override execute()
      -> request as String
      <- rtn as String: "Processed: " + request

DECORATOR VIA DELEGATION
Use trait delegation with by to create decorators:
  LoggingService with trait of Service by delegate
    delegate as Service
Override execute() to add logging before and after delegation.

STACKING DECORATORS
Wrap decorators around each other:
  core <- CoreService()
  logged <- LoggingService(core)
  validated <- ValidatingService(logged)
Calling validated.execute() runs: validation then logging then core.

ORDER MATTERS
The outermost decorator runs first. Stack in the order you want execution:
  ValidatingService(LoggingService(CoreService()))
This validates first, then logs, then processes.

VS AOP FRAMEWORKS
No separate AOP framework needed. Trait delegation with selective override provides the same cross-cutting capability with explicit, debuggable code.

See Q106 for trait basics. See Q115 for dynamic classes as lightweight decorators. See Q210 for trait delegation with by. See Q213 for mutex lock as a cross-cutting thread safety pattern.

See Q337 for transaction aspects. See Q334 for transaction delegation.

defines module qa.patterns.crosscutting

  defines trait

    Service
      execute() as abstract
        -> request as String
        <- rtn as String?

  defines class

    CoreService with trait of Service

      override execute()
        -> request as String
        <- rtn as String: "Processed: " + request

      default operator ?

    // Decorator: adds logging around the delegated call
    LoggingService with trait of Service by delegate
      delegate as Service: CoreService()

      LoggingService()
        -> svc as Service
        this.delegate: svc

      override execute()
        -> request as String
        <- rtn as String?
        Stdout().println("[LOG] Before: " + request)
        rtn: delegate.execute(request)
        Stdout().println("[LOG] After: " + $rtn)

      default operator ?

    // Decorator: adds validation before the delegated call
    ValidatingService with trait of Service by delegate
      delegate as Service: CoreService()

      ValidatingService()
        -> svc as Service
        this.delegate: svc

      override execute()
        -> request as String
        <- rtn as String: String()
        if request?
          rtn: delegate.execute(request)

      default operator ?

  defines program

    CrossCuttingDemo()
      stdout <- Stdout()

      // === CORE SERVICE ALONE ===

      core <- CoreService()
      stdout.println(core.execute("plain"))

      // === SINGLE DECORATOR ===

      logged <- LoggingService(core)
      stdout.println(logged.execute("with-logging"))

      // === STACKED DECORATORS ===
      // Validation runs first, then logging, then core

      stacked <- ValidatingService(LoggingService(CoreService()))
      stdout.println(stacked.execute("fully-decorated"))

      stdout.println("Cross-cutting via decorator stacking")

Java: AOP frameworks (Spring AOP, AspectJ), proxy-based decorators, annotation-driven aspects. Python: decorators (@decorator syntax), metaclasses, monkey patching. Rust: no built-in AOP, middleware pattern, tower crate for service layers. Go: middleware functions, handler wrapping. Kotlin: delegated properties, class delegation with by. EK9: trait delegation with by for decorator stacking, selective override for cross-cutting behavior, no AOP framework needed.

advice aspect concern cross-cutting decorator delegate design idiom logging middleware pattern stack validation weave wrap

Explanation: When overriding a trait method in a delegating class, the 'override' keyword is required. LoggingService and ValidatingService must use 'override execute()' to customize the delegated Service method. See ek9 -h E05120 for details.

Explanation: Class fields must be initialized inline with a default value. Declaring a field without an initializer triggers E08180. Provide a default like 'CoreService()' even if a constructor will override it. See ek9 -h E08180 for details.


Question: What are common EK9 anti-patterns to avoid?

What mistakes should I avoid when writing EK9 code?

Answer:
Here are common anti-patterns in EK9 with their corrections.

1. OVERUSING ANY
Bad: Taking Any parameters everywhere loses type safety. The compiler cannot check operations at compile time and dispatchers must handle every type.
Good: Use specific types or define a trait. When you need polymorphism, traits give you a controlled contract.

2. DEEP INHERITANCE HIERARCHIES
Bad: Chains of A extends B extends C extends D create fragile, tightly coupled code. EK9 types are closed by default for this reason.
Good: Use composition. Wrap the inner type as a field and expose only the methods you need. Use trait delegation to forward methods automatically.

3. IGNORING GUARD EXPRESSIONS
Bad: Assuming a value is always set and using it directly. This leads to operating on unset values with undefined results.
Good: Always use guard expressions for Optional and Result access. The guard ensures the block only executes when the value is set.

4. EXCEPTIONS FOR EXPECTED FAILURES
Bad: Using try/catch for validation, parsing, or user input errors. Exceptions are for unexpected failures.
Good: Use Result for expected failures. Result makes the caller handle both success and error paths explicitly.

5. MUTABLE SHARED STATE
Bad: Multiple threads accessing the same mutable variable without synchronization.
Good: Use MutexLock to protect shared state. The lock ensures only one thread accesses the state at a time.

6. GIANT MONOLITHIC PROGRAMS
Bad: One massive program block with hundreds of lines doing everything.
Good: Decompose into small, focused functions. Mark functions as pure where possible for testability and compiler verification.

See Q29 for guard expressions and tri-state. See Q212 for composition over inheritance. See Q213 for mutex lock thread safety. See Q254 for when Any is appropriate. See Q259 for Result vs exceptions. See Q313 for code smell detection including god classes and data clumps.

defines module qa.patterns.antipatterns

  defines trait

    // GOOD: Specific trait instead of Any
    Describable
      describe() as abstract
        <- rtn as String?

  defines class

    // GOOD: Implement the trait for type safety
    Product with trait of Describable
      productName as String?

      default private Product()

      Product()
        -> pName as String
        this.productName :=? String(pName)

      override describe()
        <- rtn as String: String(productName)

      default operator ?

    // GOOD: Composition over deep inheritance
    OrderSummary
      items as List of String: List() of String

      addItem()
        -> item as String
        items += item

      itemCount() as pure
        <- rtn as Integer: length items

      summary() as pure
        <- rtn as String: `Order with ${length items} items`

      default operator ?

  defines program

    AntiPatternsDemo()
      stdout <- Stdout()

      // === GOOD: Specific types via trait ===

      product <- Product("Widget")
      stdout.println(product.describe())

      // === GOOD: Composition wrapper ===

      order <- OrderSummary()
      order.addItem("Item A")
      order.addItem("Item B")
      order.addItem("Item C")
      stdout.println(order.summary())

      // === GOOD: Guard expression ===

      parsed <- Integer("not-a-number")
      if validNum <- parsed
        stdout.println(`Valid: ${validNum}`)
      else
        stdout.println("Invalid input handled safely")

      // === GOOD: Guarded assignment for defaults ===

      setting <- String()
      setting :=? "safe-default"
      stdout.println(`Setting: ${setting}`)

      stdout.println("Anti-patterns avoided")

Java: null everywhere, deep inheritance (AbstractFactoryBeanProcessor), checked exception abuse, mutable shared state, God classes. Python: duck typing hides type errors, mutable default arguments, global state, bare except clauses. Go: error return values ignored, interface pollution, goroutine leaks. Rust: fighting the borrow checker instead of redesigning, unwrap() everywhere, unsafe blocks. EK9: anti-patterns center on overusing Any (losing type safety), ignoring guards (operating on unset values), and inheritance over composition (fragile hierarchies).

anti-pattern avoid bad common design error idiom mistake pattern pitfall practice smell wrong

Explanation: Renaming the variable means later references to 'order' become unresolved, triggering E50001. Variable names must be consistent. See ek9 -h E50001 for details.

Explanation: Renaming the variable means later references to 'product' become unresolved, triggering E50001. Variable names must be consistent. See ek9 -h E50001 for details.


Question: How does EK9 prevent common security vulnerabilities?

What security vulnerabilities does EK9 prevent by design?

How does EK9 language design prevent attacks?

Answer:
EK9 prevents entire classes of vulnerabilities through language design decisions, not just libraries or best practices. Several OWASP top 10 categories are addressed at the language level.

A01 BROKEN ACCESS CONTROL
Types are closed by default (no 'as open' means cannot extend). There is no reflection API to bypass access modifiers. Private fields and methods cannot be accessed externally.

A03 INJECTION
The 'sanitized' keyword tracks tainted input at compile time. Seven runtime detectors covering 1,300+ patterns detect SQL injection, XSS, command injection, path traversal, LDAP injection, header injection, and expression injection. The compiler enforces the copy constructor pattern for processing sanitized data.

A04 INSECURE DESIGN
Pure functions prevent uncontrolled side effects. The tri-state model (absent/unset/set) eliminates null pointer exceptions. No break, continue, or return statements prevents control flow bugs. Guard expressions enforce safe value access before use.

A05 SECURITY MISCONFIGURATION
Constrained types enforce valid configuration at construction time. Invalid values produce unset objects that guards catch, making misconfiguration impossible to silently propagate.

A08 DATA INTEGRITY FAILURES
Immutability by default protects data. Pure functions cannot use mutation operators (+=, -=, :=:, :~:, :^:). The :=: copy, :~: merge, and :^: replace operators have defined semantics preventing accidental data corruption.

A10 SERVER-SIDE REQUEST FORGERY
Sanitized parameter tracking on URL and path parameters ensures that user-provided URLs cannot be used without going through the defensive copy pattern.

See Q29 for tri-state model. See Q54 for pure functions. See Q74 for guard expressions. See Q101 for closed types. See Q144 for no break/continue/return. See Q215 for sanitized parameters. See Q216 for threat detection. See Q218 for security best practices. See Q257 for constrained types. See Q270 for supply chain security. See Q272 for defense in depth.

defines module qa.security.owasp

  defines function

    // Pure function prevents side effects (A04)
    validateConfig() as pure
      -> configValue as String
      <- valid as Boolean: configValue?

    // Sanitized parameter prevents injection (A03)
    processExternalInput() as pure
      -> input as sanitized String
      <- result as String?

      // Copy constructor — defensive copy
      safeCopy <- String(input)
      result: "Processed: " + safeCopy

  defines program

    OwaspPreventionDemo()
      stdout <- Stdout()

      // === CLOSED TYPES (A01) ===

      stdout.println("A01: Types are closed by default")
      stdout.println("  No reflection API to bypass access")

      // === INJECTION PREVENTION (A03) ===

      userInput <- "external data"
      result <- processExternalInput(userInput)
      if result?
        stdout.println("A03: " + result)

      // === NO NULL (A04) ===

      name <- String()
      if name?
        stdout.println("Has value")
      else
        stdout.println("A04: Unset detected safely, no null pointer")

      // === GUARD EXPRESSIONS (A04) ===

      if valid <- validateConfig("production")
        stdout.println("A04: Config validated via guard: " + $valid)

      // === IMMUTABILITY (A08) ===

      stdout.println("A08: Pure functions prevent mutation operators")
      stdout.println("EK9 prevents OWASP categories by design")

Java: open by default, reflection bypasses access control, relies on OWASP libraries, NullPointerException everywhere. Python: no access control enforcement, no compile-time taint tracking, everything is mutable. Rust: ownership model prevents some categories, no taint tracking. Go: no generics until recently, no taint tracking, no purity. Kotlin: null safety helps but open by default, no sanitization. EK9: closed types, sanitized keyword, pure functions, tri-state model, no null, constrained types, guard expressions prevent multiple OWASP categories by design.

attack cve design language owasp prevention protect safe secure security vulnerability

Explanation: Renaming the variable means later references to 'result' become unresolved, triggering E50001. Variable names must be consistent. See ek9 -h E50001 for details.

Explanation: In a pure function, the mutation operator += is forbidden. Use reassignment with : to create a new value instead. See ek9 -h E08120 for details.


Question: How does EK9 handle input validation?

What input validation mechanisms does EK9 provide?

How do constrained types and guards work together for validation?

Answer:
EK9 provides four validation mechanisms that work together: constrained types for structural validation, guard expressions for conditional execution, require statements for preconditions, and sanitized parameters for taint tracking.

CONSTRAINED TYPES
Constrained types validate at construction time. A bare constructor ASSERTS the value is valid: a set value that violates the constraint panics at runtime (and a literal constant that provably violates is the compile error E08260), so use a bare constructor only for values you already trust:
  EmailAddress as String constrain as
    matches /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
  port <- Port(8080)
For untrusted or boundary input use the fallible .of(value) factory instead. It takes the base-type value and returns an UNSET object if the value violates the constraint (or is itself unset), and never panics.

GUARD EXPRESSIONS
Guards combine assignment with set-checking in one line:
  if email <- EmailAddress().of(userInput)
    processEmail(email)
The block only executes if construction succeeds. This prevents processing of invalid data.

REQUIRE STATEMENTS
Require validates preconditions at the start of a function:
  require input?
  require length input > 0
A failed require throws an exception, enforcing the contract.

SANITIZED PARAMETERS
The sanitized modifier tracks external input through the system:
  -> userInput as sanitized String
The compiler enforces the copy constructor pattern for safe handling.

FOUR LAYERS TOGETHER
The complete validation flow uses all four:
  1. sanitized marks the input as external
  2. Constrained type validates the format
  3. Guard expression checks construction succeeded
  4. require enforces additional business rules
Each layer catches a different class of invalid input.

See Q215 for sanitized parameters. See Q257 for constrained types. See Q29 for unset variables and guards. See Q74 for guard expressions. See Q268 for OWASP vulnerability prevention. See Q272 for defense in depth.

defines module qa.security.inputvalidation

  defines type

    // Constrained type: validates format at construction
    EmailAddress as String constrain as
      matches /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/

    // Constrained type: validates range
    Port as Integer constrain
      >= 1 and <= 65535

  defines function

    // All four validation layers
    validateAndProcess() as pure
      -> input as sanitized String
      <- result as String: "Invalid"

      // Layer 1: sanitized marks input as external (parameter)
      // Layer 2: copy constructor + constrained type validates
      safeCopy <- String(input)

      // Layer 3: guard checks fallible construction succeeded
      if email <- EmailAddress().of(safeCopy)

        // Layer 4: require enforces business rules
        require length $email > 5

        result: "Valid email: " + $email

  defines program

    InputValidationDemo()
      stdout <- Stdout()

      // === CONSTRAINED TYPE VALIDATION ===

      if email <- EmailAddress().of("user@example.com")
        stdout.println("Valid: " + $email)

      if badEmail <- EmailAddress().of("not-an-email")
        stdout.println("Should not reach here (email)")
      else
        stdout.println("Invalid email caught by constraint")

      // === PORT RANGE VALIDATION ===

      if port <- Port().of(8080)
        stdout.println("Valid port: " + $port)

      if badPort <- Port().of(99999)
        stdout.println("Should not reach here (port)")
      else
        stdout.println("Invalid port caught by constraint")

      // === COMBINED VALIDATION ===

      validResult <- validateAndProcess("user@example.com")
      if validResult?
        stdout.println(validResult)

      invalidResult <- validateAndProcess("bad")
      if invalidResult?
        stdout.println("Should not reach here (combined)")
      else
        stdout.println("Combined validation rejected invalid input")

Java: Bean Validation annotations, manual null checks, OWASP ESAPI. Python: no compile-time validation, runtime libraries. Rust: newtype pattern with manual validation. Go: manual if-err checks. Kotlin: nullable types and require() function. EK9: four integrated mechanisms (constrained types, guards, require, sanitized) that compose into a validation pipeline.

check constrain form guard input isset null-safe protect require safe sanitize security validate validation verify

Explanation: Renaming the variable means later references become unresolved, triggering E50001. Variable names must be consistent. See ek9 -h E50001 for details.

ValidatedUser as UserRecord constrain as
      matches /^[a-zA-Z]+$/

Explanation: Only built-in types like String, Integer, and Float can be constrained. Custom records and classes cannot be constrained because they do not have the comparison semantics needed for constraint evaluation. See ek9 -h E50010 for details.


Question: How does supply chain security work in EK9?

How does EK9 handle dependency security?

How does EK9 prevent supply chain attacks?

How does EK9 manage package trust and verification?

Answer:
EK9 addresses supply chain security through its package system, source-level compilation, and planned infrastructure for signed packages and SBOM generation.

PACKAGE DECLARATION
EK9 packages declare dependencies with explicit version constraints:
  defines package
    version 1.0.0-0
    description "My application"
    deps
      com.example.utils 2.1.0 <= v < 3.0.0
Semantic versioning is enforced. Range constraints prevent unexpected major version upgrades.

SOURCE-LEVEL COMPILATION
Unlike ecosystems where dependencies are opaque binaries (Java JARs, Python wheels, npm tarballs), EK9 compiles from source. You can inspect every line of code in your dependency tree. There are no hidden native binaries or obfuscated bytecode.

VERSION PINNING
Exact version pinning prevents the npm left-pad problem (package deletion breaks builds) and typosquatting attacks (similarly-named malicious packages):
  deps
    org.trusted.lib 1.2.3 == v

ARCHITECTURAL VISION
The EK9 supply chain security roadmap includes:
  SBOM generation: Software Bill of Materials listing all transitive dependencies
  Cryptographic signing: Package authors sign releases, consumers verify signatures
  Repository authorization: Restrict which repositories can provide packages
  Dependency auditing: Automated scanning for known vulnerabilities

COMPARISON WITH INDUSTRY INCIDENTS
npm left-pad (2016): Single package deletion broke thousands of builds. EK9's version pinning and source compilation mitigate this.
PyPI typosquatting: Malicious packages with similar names. Source-level compilation means every dependency is inspectable.
Log4Shell (2021): Hidden JNDI lookup in binary dependency. Source compilation makes hidden functionality visible.

See Q7 for dependency management. See Q15 for semantic versioning. See Q218 for security best practices. See Q268 for OWASP vulnerability prevention.

defines module qa.security.supplychain

  defines package

    version as Version: 1.0.0-0
    description as String = "Supply chain security demonstration"

  defines program

    SupplyChainDemo()
      stdout <- Stdout()

      // === PACKAGE SECURITY ===

      stdout.println("EK9 supply chain security features:")
      stdout.println("  1. Semantic versioning enforcement")
      stdout.println("  2. Source-level compilation")
      stdout.println("  3. Dependency version pinning")
      stdout.println("  4. Full dependency tree inspection")

      // === SOURCE COMPILATION ===

      stdout.println("Source compilation advantages:")
      stdout.println("  No opaque binary dependencies")
      stdout.println("  Every line of code is inspectable")
      stdout.println("  No hidden native binaries")

      // === ROADMAP ===

      stdout.println("Planned security infrastructure:")
      stdout.println("  SBOM generation")
      stdout.println("  Cryptographic package signing")
      stdout.println("  Repository authorization")

Java: Maven Central with binary JARs, no built-in signing verification, Log4Shell incident. Python: PyPI with binary wheels, frequent typosquatting. JavaScript: npm with tarballs, left-pad incident, frequent supply chain attacks. Rust: crates.io with source, better model but still vulnerable. Go: module proxy with checksums, good verification. EK9: source-level compilation, semantic versioning enforcement, planned SBOM and signing infrastructure.

chain dependency migrate npm package protect safe sbom sign supply trust version vulnerability

Explanation: Renaming the variable means later references to 'stdout' become unresolved, triggering E50001. Variable names must be consistent. See ek9 -h E50001 for details.


Question: How do I handle secrets and environment configuration?

How do I read environment variables in EK9?

How does EK9 handle API keys and credentials?

What is the best way to manage secrets in EK9?

Answer:
EK9 handles secrets through the EnvVars type, which provides auto-sanitized access to environment variables. This aligns with the 12-factor app methodology and container practices.

ENVVARS API
The EnvVars type provides:
  env <- EnvVars()
  env.get(name)            Auto-sanitized: blocks SQL, XSS, command injection but allows system paths
  env.unsanitizedGet(name) Raw value with no sanitization
  env contains name        Check if variable exists
  env.keys()               Iterate all variable names

AUTO-SANITIZATION
The get() method runs InputSanitizer automatically. This blocks injection patterns in environment variable values while allowing legitimate system paths like /usr/bin. Use unsanitizedGet() only when you specifically need raw values.

GUARD PATTERN
Combine EnvVars with guard expressions for safe access:
  env <- EnvVars()
  if apiKey <- env.get("API_KEY")
    processWithKey(apiKey)
  else
    stderr.println("API_KEY not configured")
The guard handles both missing variables and sanitization failures in one line.

SANITIZED FORWARDING
Pass secrets to functions via sanitized parameters to prevent accidental logging:
  connectToService()
    -> apiKey as sanitized String
The compiler tracks the tainted status through the call chain.

WHAT NOT TO DO
Never embed secrets in source code. Never log secret values (sanitized tracking helps prevent this). Never store secrets in the .ek9 build directory. Never use unsanitizedGet without a specific reason.

INDUSTRY ALIGNMENT
Environment variables are the industry standard for secrets. Docker and Kubernetes inject secrets as env vars. Vault and AWS Secrets Manager ultimately deliver values through env vars. The 12-factor app methodology specifies env vars for configuration. EK9's EnvVars type with auto-sanitization goes beyond what most languages provide.

See Q215 for sanitized parameters. See Q29 for unset variables and guard patterns. See Q74 for guard expressions. See Q268 for OWASP vulnerability prevention. See Q272 for defense in depth.

defines module qa.security.secrets

  defines function

    // Accept sanitized secret for processing
    connectToService() as pure
      -> apiKey as sanitized String
      <- result as String: "No key provided"

      // Copy constructor for defensive copy
      safeKey <- String(apiKey)
      if safeKey?
        result: "Connected with key length: " + $length safeKey

  defines program

    SecretsAndEnvDemo()
      stdout <- Stdout()
      stderr <- Stderr()

      // === READ ENVIRONMENT VARIABLES ===

      env <- EnvVars()

      // Auto-sanitized access with guard
      if path <- env.get("PATH")
        stdout.println("PATH available, length: " + $length path)
      else
        stdout.println("PATH not available")

      // === CHECK EXISTENCE ===

      if env contains "HOME"
        stdout.println("HOME is configured")

      // === GUARD PATTERN FOR SECRETS ===

      if apiKey <- env.get("API_KEY")
        // Pass as sanitized to maintain tracking
        result <- connectToService(apiKey)
        if result?
          stdout.println(result)
      else
        stderr.println("API_KEY not configured")

      // === ITERATE AVAILABLE KEYS ===

      stdout.println("Environment variable access patterns:")
      stdout.println("  get() — auto-sanitized")
      stdout.println("  unsanitizedGet() — raw value")
      stdout.println("  contains — check existence")
      stdout.println("  keys() — iterate names")

Java: System.getenv() returns raw String, no sanitization. Python: os.environ with no safety. Go: os.Getenv returns raw string. Rust: std::env::var returns raw string. Kotlin: System.getenv() with no sanitization. EK9: EnvVars.get() auto-sanitizes blocking injection patterns, unsanitizedGet() for raw access, guard pattern for missing variables.

api config credential env environment key password protect safe secret security sensitive token variable

Explanation: Renaming the variable means later references to 'env' become unresolved, triggering E50001. Variable names must be consistent. See ek9 -h E50001 for details.


Question: How do I build defense in depth in EK9?

How do EK9 security features work together?

How do I create a layered security pipeline in EK9?

How do I combine sanitized, constrained, require, and pure in EK9?

Answer:
Defense in depth means applying multiple security layers so that if one layer fails, the next catches the problem. EK9's security features compose naturally into a layered pipeline.

LAYER 1: SANITIZED ENTRY POINT
Mark untrusted input at the system boundary:
  processRequest()
    -> input as sanitized String
The compiler tracks this input through all subsequent operations.

LAYER 2: STRUCTURAL VALIDATION
Use a constrained type to validate the format:
  ValidEmail as String constrain as
    matches /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
Validate untrusted input with the fallible factory ValidEmail().of(value): it produces an unset object on a constraint failure (a bare ValidEmail(value) instead ASSERTS validity and Panics on a set invalid value).

LAYER 3: GUARD CHECK
Use a guard expression to verify validation succeeded:
  safeCopy <- String(input)
  if email <- ValidEmail().of(safeCopy)
    // Only proceeds with structurally valid data

LAYER 4: BUSINESS VALIDATION
Use require for domain-specific rules:
  require length $email > 5
  require $email contains "@company.com"
Failed require throws, preventing invalid business data.

LAYER 5: PURE PROCESSING
Process validated data in a pure function:
  pureTransform() as pure
    -> data as String
    <- result as String?
Purity prevents side effects and mutation during processing.

LAYER 6: OUTPUT GUARD
Guard the output before use:
  if result <- pureTransform(cleanData)
    stdout.println(result)
Ensures processing produced a valid result.

Each layer catches a different failure mode: Layer 1 catches missing sanitization at compile time. Layer 2 catches malformed input. Layer 3 prevents processing unset values. Layer 4 enforces business rules. Layer 5 prevents side effects. Layer 6 catches processing failures.

See Q54 for pure functions. See Q74 for guard expressions. See Q215 for sanitized parameters. See Q218 for security best practices. See Q257 for constrained types. See Q269 for input validation. See Q273 for purity as security boundary.

defines module qa.security.defenseindepth

  defines type

    // Layer 2: Structural validation
    ValidEmail as String constrain as
      matches /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/

  defines function

    // Layer 5: Pure processing
    pureTransform() as pure
      -> content as String
      <- result as String: "Empty"

      if content?
        result: "Processed: " + content

    // Complete defense-in-depth pipeline
    processRequest()
      // Layer 1: sanitized entry point
      -> input as sanitized String
      <- result as String: "Rejected"

      // Layer 2+3: Copy, validate via the fallible factory, and guard
      safeCopy <- String(input)
      if email <- ValidEmail().of(safeCopy)

        // Layer 4: Business validation
        require length $email > 5

        // Layer 5: Pure processing
        if processed <- pureTransform($email)

          // Layer 6: Output guard
          result: processed

  defines program

    DefenseInDepthDemo()
      stdout <- Stdout()

      // === FULL PIPELINE ===

      validResult <- processRequest("user@example.com")
      if validResult?
        stdout.println(validResult)

      // === LAYER 2 CATCHES BAD FORMAT ===

      invalidResult <- processRequest("not-email")
      if invalidResult?
        stdout.println("Should not reach here")
      else
        stdout.println("Bad format caught by constrained type")

      // === LAYERS SUMMARY ===

      stdout.println("Defense in depth layers:")
      stdout.println("  1. sanitized — marks untrusted input")
      stdout.println("  2. Constrained type — validates format")
      stdout.println("  3. Guard — checks construction")
      stdout.println("  4. require — business rules")
      stdout.println("  5. Pure — no side effects")
      stdout.println("  6. Output guard — validates result")

Java: defense in depth requires manual layering of OWASP ESAPI, Bean Validation, Spring Security. Python: no compile-time enforcement, runtime-only layers. Rust: ownership model provides some layers but no taint tracking. Go: manual error checking at each layer. Kotlin: null safety as one layer, but no sanitization or purity. EK9: sanitized, constrained types, guards, require, and pure compose into a natural defense-in-depth pipeline with compile-time enforcement.

chain combine compile defense depth layer migrate multi pipeline protect safe security stack

Explanation: Renaming the variable means later references to 'validResult' become unresolved, triggering E50001. Variable names must be consistent. See ek9 -h E50001 for details.

Explanation: Renaming the variable means later references to 'invalidResult' become unresolved, triggering E50001. Variable names must be consistent. See ek9 -h E50001 for details.


Question: How does purity create security boundaries?

Why does purity matter for security in EK9?

How does EK9 pragmatic purity prevent security bugs?

How do pure functions protect against data corruption?

Answer:
EK9 pragmatic purity is focused on preventing mutation-based attacks and data corruption. It is not Haskell-style 'no side effects' but a practical security boundary.

WHAT PURE PREVENTS
Mutation operators are forbidden in pure functions:
  += -= *= /= :=: :~: :^:
These modify the left-hand side in place and could affect the right-hand side as a side effect. Calling user-defined impure functions is also blocked.

WHAT PURE ALLOWS
Reassignment with : (creates new value):
  result: result * i
Loop variables naturally change:
  for i in 1 ... n
Return variables are reassigned, not mutated. ALL I/O is allowed because I/O types carry the IO marker trait. Stdout, Stderr, Stdin, EnvVars, TextFile, TCP, and UDP are all usable in pure functions.

DATA INTEGRITY
The key insight is that + creates a new value while += mutates in place. In pure functions, only + is allowed. This means parameters passed to a pure function cannot be modified as a side effect:
  pureProcess() as pure
    -> data as String
    result <- "Prefix: " + data
    // 'data' is guaranteed untouched

DEFENSIVE COPY ENFORCEMENT
With sanitized parameters in pure context, ONLY the copy constructor works:
  secureProcess() as pure
    -> input as sanitized String
    localCopy <- String(input)
    // Must use localCopy, cannot mutate input
Purity and sanitization together force the safest possible handling.

CONTROLLED CALL CHAINS
Pure functions can only call other pure functions. This creates a trust chain: if the entry point is pure, every function in the chain is pure. No impure operation can hide in the middle.

TOCTOU PREVENTION
Time-of-check-time-of-use attacks require mutating shared state between the check and the use. Since mutation operators are blocked in pure functions, the checked value cannot change before it is used.

CONSUMER vs ACCEPTOR
Consumer of T is pure (read-only access). Acceptor of T is impure (can mutate). This distinction lets APIs express whether a callback can modify data:
  process()
    -> handler as Consumer of String
    // handler CANNOT modify anything

See Q54 for pure functions and Consumer/Acceptor. See Q217 for sanitized parameter mechanics in pure context. See Q218 for security best practices. See Q241 for mutation operators. See Q272 for defense in depth. See Q560 for purity contracts and enforcement rules. See Q635 for forbidden mutation operators in pure methods.

defines module qa.security.puritysecurity

  defines function

    // Pure function with I/O — allowed
    pureWithIo() as pure
      -> message as String
      stdout <- Stdout()
      stdout.println("Pure I/O: " + message)

    // Pure function — reassignment, not mutation
    pureCalculate() as pure
      -> n as Integer
      <- result as Integer: 1

      for i in 1 ... n
        // result: result * i — reassignment with new value
        // result *= i — would be BLOCKED (mutation operator)
        result: result * i

    // Pure + sanitized = forced copy pattern
    pureSecureProcess() as pure
      -> input as sanitized String
      <- result as String?

      // Only copy constructor works here
      localCopy <- String(input)
      result: "Secure: " + localCopy

    // Consumer is pure — read only
    pureConsumerExample() as pure
      -> handler as Consumer of String
      handler("data")

  defines program

    PuritySecurityDemo()
      stdout <- Stdout()

      // === PURE WITH I/O ===

      pureWithIo("I/O is allowed in pure functions")

      // === PURE CALCULATION ===

      factorial <- pureCalculate(5)
      stdout.println("5! = " + $factorial)

      // === PURE + SANITIZED ===

      secureResult <- pureSecureProcess("external input")
      if secureResult?
        stdout.println(secureResult)

      // === CONSUMER vs ACCEPTOR ===

      stdout.println("Consumer of T: pure, read-only")
      stdout.println("Acceptor of T: impure, can mutate")

      // === SECURITY BENEFITS ===

      stdout.println("Purity security benefits:")
      stdout.println("  No mutation operators in pure")
      stdout.println("  Parameters cannot be modified as side effect")
      stdout.println("  Forced defensive copies with sanitized")
      stdout.println("  Pure call chains create trust boundaries")
      stdout.println("  TOCTOU prevented by no shared mutation")

Java: no purity concept, any method can mutate any reachable object. Python: no purity enforcement, convention only. Haskell: strict purity but no I/O in pure (uses IO monad). Rust: immutable borrows prevent mutation but no purity concept. Go: no purity. Kotlin: no purity enforcement. EK9: pragmatic purity blocks mutation operators while allowing I/O, creating security boundaries that prevent data corruption and TOCTOU attacks.

audit data define immutable integrity migrate mutation operator pragmatic protect pure purity safe security side-effect toctou

Explanation: In a pure function, the mutation operator *= is forbidden. Use reassignment with : to create a new value (result: result * i). See ek9 -h E08120 for details.

Explanation: Renaming the variable means later references to 'factorial' become unresolved, triggering E50001. Variable names must be consistent. See ek9 -h E50001 for details.

secureResultXYZ <- pureSecureProcess("external input")

Explanation: Renaming the variable means later references to 'secureResult' become unresolved, triggering E50001. Variable names must be consistent. See ek9 -h E50001 for details.


Question: Why does AI generate return statements in EK9?

Why does my AI assistant use return in EK9 code?

How do I fix AI-generated return statements in EK9?

What replaces the return keyword in EK9?

Answer:
AI language models trained on Java, Python, and Rust confidently generate return statements in EK9. This is one of the most common AI hallucinations because every mainstream language uses return. EK9 has NO return keyword. It does not exist in the grammar.

THE AI MISTAKE
AI generates code like 'return result', 'return "hello"', 'return;', or early return patterns like 'if invalid then return'. None of these compile in EK9. The keyword does not exist.

THE EK9 WAY
Declare a named return variable with '<-' and the compiler ensures all code paths initialise it:
  greet() as pure
    -> name as String
    <- message as String: "Hello, " + name
The return variable 'message' is declared, initialised, and automatically returned. No return statement needed.

CONDITIONAL RETURNS
When you need different values on different paths, declare a default then modify:
  classify() as pure
    -> score as Integer
    <- label as String: "average"
    if score >= 90
      label: "excellent"
    else if score < 40
      label: "poor"
The compiler verifies every path initialises 'label'. No early return needed.

REPLACING EARLY RETURN
AI often generates early return for precondition checks. In EK9, use structured control flow:
  process() as pure
    -> items as List of String
    <- count as Integer: 0
    if ~items?
      count: -1
    else
      count: length items
All paths explicitly set the return variable. The compiler enforces completeness.

GUARD EXPRESSIONS
For conditional processing where you only proceed if a value is set:
  lookup()
    -> key as String
    <- found as String: String()
    if record <- findRecord(key)
      found: $record
The guard 'if record <- findRecord(key)' checks isSet and assigns in one step. If unset, the block is skipped.

WHY REMOVED
Return statements create hidden exit points. Functions with multiple returns are harder to reason about, harder to debug, and produce unreachable code. Named return variables make every path explicit and verifiable.

See Q50 for declared return variables. See Q144 for why return was removed. See Q281 for verifying AI code. See Q289 for before and after migration patterns.

defines module qa.ai.mistakes.returnstatement

  defines function

    greet() as pure
      -> name as String
      <- message as String: "Hello, " + name

    classify() as pure
      -> score as Integer
      <- label as String: "average"
      excellentThreshold <- 90
      poorThreshold <- 40
      if score >= excellentThreshold
        label: "excellent"
      else if score < poorThreshold
        label: "poor"

    countItems() as pure
      -> items as List of String
      <- count as Integer: 0
      if ~items?
        count: -1
      else
        count: length items

  defines program

    ReturnMistakeDemo()
      stdout <- Stdout()

      // === SIMPLE RETURN ===

      stdout.println(greet("Steve"))

      // === CONDITIONAL RETURN ===

      stdout.println(`Score 95: ${classify(95)}`)
      stdout.println(`Score 30: ${classify(30)}`)
      stdout.println(`Score 60: ${classify(60)}`)

      // === REPLACING EARLY RETURN ===

      names <- ["Alice", "Bob"]
      stdout.println(`Count: ${countItems(names)}`)

      emptyList <- List() of String
      stdout.println(`Empty count: ${countItems(emptyList)}`)

Java: return statement required, early return common for preconditions. Python: return statement, implicit None if omitted. Rust: implicit last expression or explicit return. Go: named returns exist but return still required. JavaScript: return statement, undefined if omitted. EK9: NO return statement, named return variable with '<-', compiler verifies all paths, guard expressions replace early return.

ai common-error declare early exit hallucination migrate mistake pitfall precondition return statement variable wrong

Explanation: EK9 has no return keyword. Declare a named return variable with '<-' instead. The compiler enforces all code paths initialise it. See ek9 -h E01072 for details.

Explanation: EK9 uses declared return variables, not return statements. Assign different values on different code paths and the compiler verifies completeness. See ek9 -h E01072 for details.

Explanation: AI generates early return for conditional paths. In EK9, assign to the declared return variable instead. The compiler verifies all paths initialise it. No early exit needed. See ek9 -h E01072 for details.


Question: Why does AI use break and continue in EK9?

Why does my AI assistant generate break in EK9 loops?

How do I replace AI-generated break and continue in EK9?

What does EK9 use instead of break and continue?

Answer:
AI models trained on C, Java, and Python generate break and continue in EK9 loops. These keywords do not exist in EK9. They are not in the grammar. The compiler cannot parse them.

THE AI MISTAKE
AI generates 'break' to exit a loop early and 'continue' to skip items. Both are invalid EK9.

THE EK9 PHILOSOPHY
For loops in EK9 are designed as complete-iteration constructs. They run from start to finish. If you need selective or partial processing, use stream pipelines instead.

FILTER REPLACES CONTINUE
Where AI writes a loop with continue to skip items, use filter:
  cat items | filter by isValid | collect as List of String
Filter selects only matching elements. Non-matching elements are skipped. This is declarative: you say WHAT you want, not HOW to skip.

HEAD REPLACES BREAK
Where AI writes a loop with break to take the first N items, use head:
  cat items | head 5 | collect as List of String
Head takes the first N elements and stops. No loop exit needed.

TAIL AND SKIP
For other slicing patterns:
  cat items | tail 3 | collect as List of String
  cat items | skip 2 | collect as List of String
Tail takes the last N. Skip discards the first N.

FOR-IN RUNS FULLY
The for-in loop processes every element:
  for item in items
    stdout.println(item)
This is the intended pattern. Every item is processed. If you need to process only some items, filter first, then iterate.

EVIDENCE
Microsoft (2011): 15% of production bugs in C# involved break and continue errors. Apple SSL bug (2014): duplicated goto bypassed SSL validation. Linux Kernel: 200+ CVE fixes from break-in-wrong-loop bugs.

See Q89 for stream pipelines. See Q125 for head, tail, and skip. See Q144 for the design decision. See Q145 for replacing break and continue. See Q281 for verifying AI code. See Q288 for before and after migration patterns.

defines module qa.ai.mistakes.breakcontinue

  defines function

    isLong() as pure
      -> item as String
      <- longer <- Boolean()
      minLength <- 3
      longer: length item > minLength

  defines program

    BreakContinueDemo()
      stdout <- Stdout()

      items <- ["ax", "hello", "by", "world", "no", "stream"]

      // === FILTER REPLACES CONTINUE ===
      // AI would write: for item in items { if length(item) <= 3 then continue; process(item) }
      // EK9 way: filter to only long items

      longItems <- cat items | filter by isLong | collect as List of String
      for item in longItems
        stdout.println(`Long item: ${item}`)

      // === HEAD REPLACES BREAK ===
      // AI would write: for item in items { count++; if count >= 2 then break }
      // EK9 way: take first 2

      firstTwo <- cat items | head 2 | collect as List of String
      for item in firstTwo
        stdout.println(`First two: ${item}`)

      // === COMBINED: FILTER THEN HEAD ===

      firstLong <- cat items | filter by isLong | head 1 | collect as List of String
      for item in firstLong
        stdout.println(`First long: ${item}`)

      // === FOR-IN RUNS FULLY (the intended pattern) ===

      for item in items
        stdout.println(`All: ${item}`)

Java: break and continue in loops, labelled break for nested loops. Python: break and continue in loops. Rust: break and continue with loop labels. Go: break and continue. C/C++: break and continue, major bug source. EK9: NO break or continue, use stream pipelines with filter (skip items), head (take first N), tail (take last N), skip (discard first N). For loops run fully by design.

ai break common-error continue exit filter hallucination head loop migrate mistake pitfall skip stream tail wrong

Explanation: EK9 has no continue keyword. It does not exist in the grammar. Use stream pipelines with filter to skip items instead of loops with continue. See ek9 -h E01071 for details.

Explanation: EK9 has no break keyword. It does not exist in the grammar. Use head N in a stream pipeline to take the first N elements instead of loops with break. See ek9 -h E01070 for details.


Question: Why does AI confuse declarations and assignments in EK9?

Why does AI generate 'let x = 5' or 'var x = 5' in EK9?

How do declarations and assignments differ in EK9?

Answer:
AI models generate 'x = 5', 'let x = 5', 'var x = 5', or 'val x = 5' in EK9. None of these are valid EK9 declarations. AI conflates two distinct concepts: creating a new variable (declaration) and changing an existing variable (assignment).

THE AI MISTAKE
AI does not understand EK9's distinction between declaration and assignment. It imports syntax from JavaScript (let/const), Kotlin (val/var), Rust (let), or Java (type name = value).

EK9 DECLARATIONS
Create a new variable with '<-' (type inferred) or explicit type with ':' initialiser:
  name <- "hello"
  age <- 42
  score as Float: 9.5
  greeting as String: "hi"
The '<-' operator creates AND initialises. The 'as Type:' form declares with explicit type.

EK9 ASSIGNMENTS
Assign to an EXISTING variable with ':', ':=', or '=':
  name: "world"
  name := "world"
  name = "world"
All three have IDENTICAL semantics. The choice is stylistic:
  ':' is concise, natural in loop bodies, switch branches, if/else blocks
  ':=' is stronger, highlights significant or deliberate assignments
  '=' is also valid, same semantics as the other two

GUARDED ASSIGNMENT
The ':=?' operator has DIFFERENT semantics from the other three. It only assigns if the variable is currently UNSET:
  config <- String()
  config :=? "default"
  config :=? "other"
After this, config is "default". The second ':=?' does nothing because config is already set.

THE COMPILER ENFORCES THE DISTINCTION
Using '<-' on an existing variable is an error. Using ':' or ':=' on a non-existent variable is an error. The compiler knows whether you are declaring or assigning.

See Q24 for variable declaration. See Q29 for guarded assignment. See Q168 for fallback values. See Q243 for coalescing operators. See Q281 for verifying AI code.

defines module qa.ai.mistakes.assignment

  defines program

    AssignmentDemo()
      stdout <- Stdout()

      // === DECLARATIONS WITH <- ===

      name <- "Alice"
      age <- 30
      active <- true

      stdout.println(`Declared: ${name}, ${age}, ${active}`)

      // === EXPLICIT TYPE DECLARATIONS ===

      greeting as String: "Hello"
      limit as Integer: 100

      stdout.println(`Typed: ${greeting}, ${limit}`)

      // === ASSIGNMENTS (all three identical) ===

      name: "Bob"
      stdout.println(`After ':' assignment: ${name}`)

      name := "Charlie"
      stdout.println(`After ':=' assignment: ${name}`)

      name = "Diana"
      stdout.println(`After '=' assignment: ${name}`)

      // === GUARDED ASSIGNMENT :=? ===

      config <- String()
      stdout.println(`Before guard: config is set? ${config?}`)

      config :=? "default"
      stdout.println(`After first guard: ${config}`)

      config :=? "other"
      stdout.println(`After second guard: ${config}`)

      // === STYLISTIC CHOICE IN CONTEXT ===

      total <- 0
      for num in [1, 2, 3, 4, 5]
        total: total + num
      stdout.println(`Loop total with ':': ${total}`)

      strategy <- "none"
      strategy := "aggressive"
      stdout.println(`Strategy with ':=': ${strategy}`)

Java: 'Type name = value' for declaration, 'name = value' for assignment, same '=' symbol for both. JavaScript: 'let x = 5', 'const x = 5', 'x = 5'. Kotlin: 'val x = 5' (immutable), 'var x = 5' (mutable). Rust: 'let x = 5', 'let mut x = 5'. Python: 'x = 5' for both declaration and assignment. Go: ':=' for short declaration, '=' for assignment. EK9: '<-' for declaration (type inferred), 'as Type:' for explicit type, ':', ':=', '=' for assignment (all identical), ':=?' for guarded assignment (different semantics).

ai assignment common-error declaration declare equals hallucination infer let migrate mistake operator pitfall syntax var variable wrong

Explanation: Using '=' attempts to assign to an existing variable, but 'name' was never declared. In EK9, '<-' creates a new variable (declaration), while '=', ':=', and ':' assign to an EXISTING variable. AI trained on Python uses '=' for both, but EK9 enforces the distinction. See ek9 -h E50001 for details.


Question: Why does AI generate null checks instead of guards in EK9?

How do I replace AI-generated null checks in EK9?

Answer:
AI models generate 'if result != null', 'if result is not None', or 'if result == null return' in EK9. There is no null in EK9. The keyword does not exist. EK9 uses a tri-state model (absent, unset, set) with guard expressions.

THE AI MISTAKE
AI imports null-checking patterns from Java (null), Python (None), JavaScript (null/undefined), or Go (nil). None of these concepts exist in EK9.

THE EK9 TRI-STATE MODEL
Every EK9 object exists in one of three states:
  Absent: does not exist (missing Dict key, empty Optional)
  Present but unset: exists but has no meaningful value (String() creates unset String)
  Present and set: exists with a valid value (String("hello") is set)

THE ? OPERATOR
Check whether an object is set with the ? operator:
  if name?
    stdout.println(name)
This replaces null checks. The ? operator returns Boolean.

GUARD EXPRESSIONS
Guards combine assignment with the ? check in a single step:
  if record <- findRecord(key)
    stdout.println(`Found: ${record}`)
The guard 'if record <- findRecord(key)' declares 'record', assigns the result, and checks if it is set. If unset, the block is skipped entirely. No null check needed.

GUARDS WORK EVERYWHERE
The same guard syntax works identically in all control flow:
  while connection <- getActive()
    transferData(connection)
  for entry <- nextEntry()
    process(entry)
  switch config <- loadConfig()
    case .port > 8000
      useHighPort(config)

GUARDED ASSIGNMENT FOR DEFAULTS
The ':=?' operator assigns only if the target is unset:
  host <- String()
  host :=? "localhost"
This replaces the null coalescing pattern (Java's 'x != null ? x : default').

See Q29 for tri-state semantics. See Q74 for guard expressions. See Q85 for guards in all control flow. See Q243 for coalescing operators. See Q281 for verifying AI code.

defines module qa.ai.mistakes.nullchecks

  defines function

    findByName() as pure
      -> searchName as String
      <- found as String: String()
      if searchName == "Alice"
        found: "Alice: Engineer"

    getPort() as pure
      <- port as Integer: 8080

  defines program

    NullCheckDemo()
      stdout <- Stdout()

      // === GUARD EXPRESSION REPLACES NULL CHECK ===
      // AI would write: result = findByName("Alice"); if (result != null) print(result)
      // EK9 way: guard expression

      if record <- findByName("Alice")
        stdout.println(`Found: ${record}`)

      if record <- findByName("Unknown")
        stdout.println("Found someone")
      else
        stdout.println("Not found, guard skipped the block")

      // === ? OPERATOR REPLACES NULL CHECK ===
      // AI would write: if (name != null) print(name)
      // EK9 way: ? operator

      name <- String()
      if name?
        stdout.println("Has a name")
      else
        stdout.println("Name is unset (not null, just unset)")

      name: findByName("Steve")
      if name?
        stdout.println(`Name is set: ${name}`)

      // === GUARDED ASSIGNMENT REPLACES NULL COALESCING ===
      // AI would write: host = host != null ? host : "localhost"
      // EK9 way: :=? guarded assignment

      host <- String()
      host :=? "localhost"
      stdout.println(`Host: ${host}`)

      host :=? "other"
      stdout.println(`Still: ${host}`)

      // === GUARD WITH FUNCTION RETURN ===

      if port <- getPort()
        stdout.println(`Port: ${port}`)

Java: null checks everywhere, NullPointerException is most common exception, Optional since Java 8. Python: 'if x is not None', AttributeError from None. JavaScript: null AND undefined, loose equality traps. Kotlin: nullable types String?, safe call ?.  operator, Elvis ?:. Rust: Option<T> with match/if-let. Go: nil checks. EK9: NO null, tri-state model (absent/unset/set), ? operator checks isSet, guard expressions combine check and assignment, :=? guarded assignment for defaults.

absent ai check common-error guard hallucination isset migrate mistake nil none null null-safe pitfall safe set tristate unset wrong

Explanation: EK9 has no null keyword. Use guard expressions with '<-' to check if a value is set and assign it in one step. See ek9 -h E01073 for details.

Explanation: EK9 uses the ? operator to check if an object is set, not null comparisons. The ? operator returns Boolean. See ek9 -h E01073 for details.

host = host != null <- host : "localhost"

Explanation: EK9 uses the guarded assignment operator :=? which only assigns if the variable is currently unset. This replaces null coalescing patterns. See ek9 -h E01073 for details.


Question: Why does AI try to extend List, Dict, or closed types in EK9?

Why does AI generate 'MyList extends List of String' in EK9?

How do I fix AI-generated class extension of closed types?

Answer:
AI models trained on Java generate 'class MyList extends ArrayList' or 'class TypedMap extends HashMap' patterns. In EK9, types are CLOSED by default. Built-in collection types (List, Dict, Optional, Result, PriorityQueue) cannot be extended. Attempting to extend them produces error E05030.

THE AI MISTAKE
AI generates 'MyList extends List of String' or 'SafeDict extends Dict of (String, Integer)'. This fails because List and Dict are closed types. The AI is importing the Java pattern where ArrayList and HashMap are open.

THE EK9 WAY: COMPOSITION
Wrap the collection as a private field and expose only the operations you need:
  TaskQueue
    items as List of String
    addTask()
      -> taskName as String
      items += taskName
    pending() as pure
      <- count as Integer: length items
The List is hidden. Clients interact with TaskQueue, not List.

WHY CLOSED BY DEFAULT
Closed types prevent mixing collection behavior with application logic. If you extend List, callers get both your custom API and every List method. This creates confusing, hard-to-maintain interfaces. Composition gives you complete control over the public API.

ADDING CUSTOM VALIDATION
Composition lets you add business rules that inheritance cannot:
  ValidatedList
    items as List of String
    add()
      -> entry as String
      if entry?
        items += entry
Only set (valid) strings are added. With inheritance, callers could bypass your add method and use the parent List methods directly.

EXPOSING ITERATION
Delegate to the wrapped collection when needed:
  allTasks() as pure
    <- tasks as List of String: List(items)
Return a copy to prevent external modification of internal state.

MODERN PRECEDENT
Swift structs are closed. Rust has no inheritance. Kotlin classes are final by default. EK9 follows this modern trend. Types require explicit 'as open' to allow extension.

See Q96 for class definitions. See Q212 for composition over inheritance. See Q210 for trait delegation. See Q281 for verifying AI code.

defines module qa.ai.mistakes.closedtypes

  defines class

    // === COMPOSITION WRAPPING A LIST ===

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

      default TaskQueue()

      addTask()
        -> taskName as String
        if taskName?
          items += taskName

      pending() as pure
        <- count as Integer: length items

      allTasks() as pure
        <- tasks as List of String: items + List() of String

      override operator ? as pure
        <- isSet as Boolean: items?

      operator $ as pure
        <- asString as String: `TaskQueue(${pending()} tasks)`

    // === COMPOSITION WRAPPING A DICT ===

    Settings
      entries as Dict of (String, String): Dict() of (String, String)

      default Settings()

      put()
        ->
          key as String
          setting as String
        if key? and setting?
          entries += DictEntry(key, setting)

      get() as pure
        -> key as String
        <- setting as String: entries.getOrDefault(key, "")

      has() as pure
        -> key as String
        <- exists as Boolean: entries.contains(key)

      override operator ? as pure
        <- isSet as Boolean: entries?

  defines program

    ClosedTypeDemo()
      stdout <- Stdout()

      // === USE TASKQUEUE (COMPOSITION OVER INHERITANCE) ===

      queue <- TaskQueue()
      queue.addTask("Deploy")
      queue.addTask("Test")
      queue.addTask("Review")

      stdout.println($queue)
      stdout.println(`Pending: ${queue.pending()}`)

      tasks <- queue.allTasks()
      for taskItem in tasks
        stdout.println(`  Task: ${taskItem}`)

      // === USE SETTINGS (COMPOSITION OVER INHERITANCE) ===

      prefs <- Settings()
      prefs.put("theme", "dark")
      prefs.put("lang", "en")

      stdout.println(`Theme: ${prefs.get("theme")}`)
      stdout.println(`Has lang: ${prefs.has("lang")}`)
      stdout.println(`Missing: ${prefs.get("missing")}`)

Java: ArrayList and HashMap are open, commonly extended (considered bad practice). Python: list is open, commonly subclassed. C++: virtual inheritance, open by default. Rust: no inheritance, composition is the pattern. Go: embedding for delegation, no inheritance. Kotlin: classes final by default like EK9. Swift: structs are closed. EK9: ALL types closed by default, 'as open' required for extension, built-in collections cannot be extended, composition is the natural pattern.

ai closed common-error composition delegate dict exhaustive extend hallucination inherit list migrate mistake open pitfall sealed wrong

TaskQueue extends List of String
      items as List of String

Explanation: EK9 types are closed by default. List, Dict, Optional, Result, and PriorityQueue cannot be extended. Use composition instead: wrap the collection as a private field. See ek9 -h E05030 for details.

Settings extends Dict of (String, String)
      entries as Dict of (String, String)

Explanation: Dict is a closed type and cannot be subclassed. Wrap it as a field and expose domain-specific methods instead. See ek9 -h E05030 for details.


Question: Why does AI confuse EK9 operators with other languages?

Why does AI generate toString() instead of the $ operator?

What are the correct EK9 operator equivalents?

How do I fix AI-generated method calls that should be operators?

Answer:
AI models map Java and Python method names to EK9 when operators should be used. EK9 has a fixed set of operators with specific symbols. AI frequently generates method calls that do not exist.

COMMON AI MISTAKES AND CORRECT EK9
  .toString() does not exist. Use $ (the string operator). Always returns String.
  .toJson() does not exist. Use $$ (the JSON operator). Always returns JSON.
  .hashCode() does not exist. Use #? (the hashcode operator). Returns Integer.
  .equals(other) does not exist. Use == (equality operator). Returns Boolean.
  .clone() does not exist. Use :=: (copy operator) or copy constructor MyType(original).
  .isPresent() does not exist. Use ? (isSet operator). Returns Boolean.
  !x for logical NOT is wrong for Boolean. Use ~x (tilde, only for Boolean and Bits).
  x instanceof T does not exist. Use traits and dispatching instead.

OPERATOR DEFINITIONS IN A CLASS
When defining operators on your own types, use the 'operator' keyword:
  operator $ as pure
    <- rtn as String: ...
  operator #? as pure
    <- rtn as Integer: ...
  operator == as pure
    -> other as MyType
    <- rtn as Boolean: ...
  operator <=> as pure
    -> other as MyType
    <- rtn as Integer: ...

NOT-EQUAL
Both != and <> are valid for not-equal in EK9. AI often uses only != which works, but <> is also correct and preferred in some contexts.

COPY PATTERNS
Two ways to copy: the :=: operator mutates the target, the copy constructor creates a new instance:
  target :=: source
  copied <- MyType(original)

See Q238 for the complete operator set. See Q239 for comparison operators. See Q240 for arithmetic operators. See Q241 for mutation operators. See Q281 for verifying AI code.

defines module qa.ai.mistakes.operators

  defines class

    // === Class to test $ operator confusion ===

    Labelled
      label as String: String()

      Labelled()
        -> initialLabel as String
        label: initialLabel

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

      override operator ? as pure
        <- rtn as Boolean: label?

    // === Class to test #? operator confusion ===

    Coded
      code as Integer: Integer()

      Coded()
        -> initialCode as Integer
        code: initialCode

      operator #? as pure
        <- rtn as Integer: Integer(code)

      override operator ? as pure
        <- rtn as Boolean: code?

    // === Class to test == operator confusion ===

    Ranked
      rank as Integer: Integer()

      Ranked()
        -> initialRank as Integer
        rank: initialRank

      operator == as pure
        -> other as Ranked
        <- rtn as Boolean: rank == other.rank

      override operator ? as pure
        <- rtn as Boolean: rank?

  defines program

    OperatorConfusionDemo()
      stdout <- Stdout()

      // === $ REPLACES toString() ===

      item <- Labelled("hello")
      stdout.println(`Item: ${item}`)

      // === #? REPLACES hashCode() ===

      coded <- Coded(42)
      hash <- #?coded
      stdout.println(`Hash: ${hash}`)

      // === == REPLACES equals() ===

      rankA <- Ranked(1)
      rankB <- Ranked(1)
      stdout.println(`Equal: ${rankA == rankB}`)

      // === ~ FOR BOOLEAN NOT ===

      active <- true
      inactive <- ~active
      stdout.println(`Active: ${active}, Inactive: ${inactive}`)

Java: toString(), hashCode(), equals(), clone(), instanceof, !boolean. Python: str(), hash(), __eq__, copy.copy(), isinstance(), not. Rust: Display trait, Hash trait, PartialEq, Clone, matches!, !bool. Go: Stringer interface, no operator overloading. Kotlin: toString(), hashCode(), equals(), !boolean. EK9: $ for string, $$ for JSON, #? for hashcode, == for equality, :=: for copy, ? for isSet, ~ for NOT (Boolean/Bits only), traits and dispatching replace instanceof.

ai clone common-error confuse equals hallucination hashcode instanceof isset migrate mistake operator pitfall tostring wrong

Explanation: EK9 has no toString() method. Use the $ operator for string conversion. Without it, the type cannot be used in string interpolation. See ek9 -h E04020 for details.

Explanation: EK9 has no hashCode() method. Use the #? operator for hash code computation. Without it, the #? prefix operator is not available on the type. See ek9 -h E07620 for details.

Explanation: EK9 has no equals() method. Use the == operator for equality comparison. Without it, the == infix operator is not available on the type. See ek9 -h E07620 for details.

Explanation: Renaming the variable means later references to 'item' become unresolved, triggering E50001. Variable names must be consistent. See ek9 -h E50001 for details.

Explanation: Renaming the variable means later references to 'coded' become unresolved, triggering E50001. Variable names must be consistent. See ek9 -h E50001 for details.


Question: Why does AI generate getters and setters in EK9?

Why does AI use getName() and setName() in EK9?

What is the EK9 convention for accessors?

How does EK9 handle information hiding without getters?

Answer:
AI models trained on Java generate getName(), setName(), getValue(), isActive() patterns. Nothing is syntactically wrong with these names in EK9, but they violate EK9 conventions and miss the deeper design philosophy.

THE AI MISTAKE
AI generates Java bean methods: getName(), setName(value), getContent(), isActive(). EK9 does not follow this naming convention.

EK9 ACCESSOR CONVENTION
Use bare noun or verb names:
  content() not getContent()
  keys() not getKeys()
  hue() not getHue()
  trim() not getTrim()

EK9 SETTER ALTERNATIVE
Use bare noun with parameter or 'with' prefix:
  content(newContent) not setContent(newContent)
  withContent(newContent) for returning a modified copy

THE DEEPER ISSUE
Get and set patterns encourage breaking information hiding. You should be interested in a class's BEHAVIOR, not its internal state. Classes and components should keep internal state private and expose operations that make sense in the domain.

EXTERNAL STATE REPRESENTATION
If external state is needed, return a RECORD with copies of an external representation, not copies of internal state variables. The record is a deliberate public-facing view, decoupled from internals. If the internal structure changes, the record stays stable.

OPERATORS NOT GETTERS
For standard conversions, use operators:
  $ for string representation (replaces toString/getName patterns)
  $$ for JSON representation
  ? for state checking (replaces isActive/isValid patterns)

ONLY ENUMERATIONS HAVE AUTOMATIC OPERATORS
Only enumerations have full automatic operator generation. Records and classes can use 'default operator' to opt in, but this is explicit, not automatic.

See Q96 for classes. See Q98 for records. See Q238 for operator overview. See Q281 for verifying AI code.

defines module qa.ai.mistakes.getterssetters

  defines record

    // Record as external state representation

    AccountSummary
      holder as String: String()
      balance as Float: Float()

      AccountSummary() as pure
        ->
          theHolder as String
          theBalance as Float
        holder :=? theHolder
        balance :=? theBalance

      default operator

  defines class

    // Class exposes BEHAVIOR, not internal state

    BankAccount
      ownerName as String: String()
      currentBalance as Float: Float()
      isOpen as Boolean: false

      BankAccount()
        ->
          initialOwner as String
          initialBalance as Float
        ownerName: initialOwner
        currentBalance: initialBalance
        isOpen: true

      // Bare noun accessor — NOT getOwnerName()
      owner() as pure
        <- rtn as String: String(ownerName)

      // Behavior method — NOT setBalance()
      deposit()
        -> amount as Float
        if amount > 0.0 and isOpen
          currentBalance: currentBalance + amount

      withdraw()
        -> amount as Float
        <- success as Boolean: false
        if amount > 0.0 and amount <= currentBalance and isOpen
          currentBalance: currentBalance - amount
          success: true

      closeAccount()
        isOpen: false

      // Return a RECORD as external state view
      summary() as pure
        <- rtn as AccountSummary: AccountSummary(ownerName, currentBalance)

      // $ operator replaces toString()
      operator $ as pure
        <- rtn as String: `${ownerName}: ${currentBalance}`

      // ? operator replaces isActive()
      override operator ? as pure
        <- rtn as Boolean: ownerName? and isOpen

  defines program

    GetterSetterDemo()
      stdout <- Stdout()

      account <- BankAccount("Alice", 1000.0)

      // === BARE NOUN ACCESSOR — NOT getName() ===

      stdout.println(`Owner: ${account.owner()}`)

      // === BEHAVIOR METHODS — NOT setBalance() ===

      account.deposit(500.0)
      stdout.println(`After deposit: ${account}`)

      if ok <- account.withdraw(200.0)
        stdout.println(`Withdrew 200: ${account}`)

      // === RECORD AS EXTERNAL STATE VIEW ===

      view <- account.summary()
      stdout.println(`Summary: ${view}`)

      // === ? OPERATOR REPLACES isActive() ===

      stdout.println(`Account active: ${account?}`)
      account.closeAccount()
      stdout.println(`After close, active: ${account?}`)

Java: JavaBean convention (getName/setName), IDE generates getters/setters, Lombok @Data generates them. Python: @property decorator, direct attribute access common. Rust: no getter convention, pub fields or methods. Go: exported fields (uppercase) or accessor methods. Kotlin: properties with get/set, data classes auto-generate. C#: properties with get/set accessors. EK9: bare noun accessors (content() not getContent()), operators for standard conversions ($ for string, ? for state), expose behavior not state, records for external state representation.

accessor ai bean behavior common-error encapsulation field getter hallucination hiding information migrate mistake pitfall property setter wrong

accountXYZ <- BankAccount("Alice", 1000.0)

Explanation: Renaming the variable means later references to 'account' become unresolved, triggering E50001. Variable names must be consistent. See ek9 -h E50001 for details.

Explanation: Renaming the variable means later references to 'view' become unresolved, triggering E50001. Variable names must be consistent. See ek9 -h E50001 for details.


Question: How do I verify AI-generated EK9 code?

How do I check if AI-generated EK9 code is correct?

What is the workflow for validating AI EK9 output?

How do I use the compiler to review AI code?

Answer:
The EK9 compiler catches ALL common AI mistakes. Three-step verification:

STEP 1: COMPILE — Run 'ek9 -c file.ek9'. Catches syntax, types, operators, code flow.

STEP 2: FIX ERRORS — Use 'ek9 -h String', 'ek9 -h List' for correct APIs. Use 'ek9 -q "topic"' for patterns.

STEP 3: TEST — Use @Test and assert, run 'ek9 -t file.ek9' for logic errors.

COMMON AI FIXES
  'return x' -> '<- rtn as Type: x'
  'break' -> 'head N' in stream
  'if x != null' -> 'if x <- expr()' (guard)
  'extends List' -> delegation
  '.toString()' -> '$' operator
  lambda -> dynamic function

See Q274-Q280 for specific AI mistake categories. See Q52 for dynamic functions. See Q115 for dynamic classes. See Q156 for assert.

defines module qa.ai.mistakes.verify

  defines function

    // Declared return — AI would write 'return'
    classify() as pure
      -> score as Integer
      <- rating as String: "average"
      excellentThreshold <- 90
      poorThreshold <- 40
      if score >= excellentThreshold
        rating: "excellent"
      else if score < poorThreshold
        rating: "poor"

    // Pure predicate for stream filter — replaces 'continue'
    isPositive() as pure
      -> number as Integer
      <- positive as Boolean: number > 0

    // Abstract function type — dynamic functions implement these
    transformer() as pure abstract
      -> input as Integer
      <- output as Integer?

  defines trait

    Formatter
      format() as abstract
        -> input as String
        <- rtn as String?

  defines class

    // Composition — AI would write 'extends List of String'
    NameList
      items as List of String: List() of String

      default NameList()

      add()
        -> entry as String
        if entry?
          items += entry

      count() as pure
        <- total as Integer: length items

      allNames() as pure
        <- names as List of String: items + List() of String

      // $ operator — AI would write toString()
      operator $ as pure
        <- rtn as String: `NameList(${count()} names)`

      // ? operator — AI would write isPresent() or isActive()
      override operator ? as pure
        <- rtn as Boolean: items?

  defines program

    VerifyAiCodeDemo()
      stdout <- Stdout()

      // === DECLARED RETURN (not 'return') ===

      stdout.println(`Score 95: ${classify(95)}`)
      stdout.println(`Score 30: ${classify(30)}`)

      // === STREAM PIPELINE (not break/continue) ===

      numbers <- [-2, 5, -1, 8, 0, 3]
      positives <- cat numbers | filter by isPositive | head 2 | collect as List of Integer
      for num in positives
        stdout.println(`Positive: ${num}`)

      // === GUARD EXPRESSION (not null check) ===

      if rating <- classify(75)
        stdout.println(`Rating: ${rating}`)

      // === COMPOSITION (not extends List) ===

      names <- NameList()
      names.add("Alice")
      names.add("Bob")
      stdout.println($names)
      stdout.println(`Count: ${names.count()}`)

      // === GUARDED ASSIGNMENT (not null coalescing) ===

      config <- String()
      config :=? "production"
      stdout.println(`Config: ${config}`)

      // === ? OPERATOR (not isPresent/isActive) ===

      stdout.println(`Names set: ${names?}`)

      // === DYNAMIC FUNCTION (not lambda '(x) -> x + x') ===

      doubler <- () is transformer as pure function
        output:=? input + input

      testInput <- 21
      stdout.println(`Doubled: ${doubler(testInput)}`)

      // === UNNAMED DYNAMIC CLASS (not 'new Interface() { }') ===

      tag <- ">>>"
      fmtHandler <- (tag) with trait of Formatter as class
        override format()
          -> input as String
          <- rtn as String: `${tag} ${input}`
        default operator ?

      stdout.println(fmtHandler.format("hello"))

      // === NAMED DYNAMIC CLASS (not inline 'class Pair { }') ===

      userName <- "Carol"
      userAge <- 30
      person <- PersonInfo(label: userName, personAge: userAge) as class
        describe() as pure
          <- rtn as String: `${label} age ${personAge}`
        operator $ as pure
          <- rtn as String: describe()
        default operator ?

      stdout.println($person)

      // === BARE NOUN ACCESSOR (not getCount/getNames) ===

      allItems <- names.allNames()
      for item in allItems
        stdout.println(`  Name: ${item}`)

    // === STEP 6: WRITE TESTS TO VERIFY AI LOGIC ===

    @Test
    VerifyClassify()
      assert classify(95) == "excellent"
      assert classify(30) == "poor"
      assert classify(60) == "average"

    @Test
    VerifyNameList()
      names <- NameList()
      names.add("Alice")
      names.add("Bob")
      expectedCount <- 2
      assert names.count() == expectedCount
      assert names?

    @Test
    VerifyDynamicFunction()
      doubler <- () is transformer as pure function
        output:=? input + input
      testInput <- 21
      expectedResult <- 42
      assert doubler(testInput) == expectedResult

    @Test
    VerifyDynamicClass()
      tag <- ">>>"
      fmtHandler <- (tag) with trait of Formatter as class
        override format()
          -> input as String
          <- rtn as String: `${tag} ${input}`
        default operator ?
      expectedOutput <- ">>> test"
      assert fmtHandler.format("test") == expectedOutput

Java/Python/JS: no equivalent compile-time AI code verification. EK9: compiler catches ALL common AI mistakes with specific error codes, built-in help and Q&A system for correct patterns.

ai anonymous assistant capture check class code common-error compile correct dynamic error function hallucination lambda mistake pitfall review tool trait validate verify

Explanation: Renaming the variable means later references to 'names' become unresolved, triggering E50001. Variable names must be consistent. See ek9 -h E50001 for details.

Explanation: Renaming the variable means later references to 'config' become unresolved, triggering E50001. Variable names must be consistent. See ek9 -h E50001 for details.

Explanation: Renaming the variable means later references to 'tag' become unresolved, triggering E50001. Variable names must be consistent. See ek9 -h E50001 for details.

Explanation: Methods implementing trait abstracts in a dynamic class must use 'override' keyword. Without it the method shadows the parent rather than implementing it. See ek9 -h E05120 for details.


Control Flow Without break/continue/return

Question: How do I exit a nested loop without break or labels?

How do I stop both inner and outer loops without break in EK9?

What replaces labelled break for nested loops in EK9?

How do I search a grid or matrix without break in EK9?

Answer:
EK9 has no break, no labels, and no goto. When you need to exit nested loops, the solution is function decomposition: extract the inner loop into its own function.

THE PROBLEM
In Java you write: outer: for (row : grid) { for (item : row) { if (found(item)) break outer; } }. In Python you use flag variables or exceptions. These patterns are error-prone and obscure intent.

PATTERN 1: DECOMPOSE INTO FUNCTIONS
Extract the inner loop into a separate function that returns unset if nothing found:
  searchRow()
    -> row as List of String, target as String
    <- found as String: String()
    for item in row
      if item == target
        found: item
Then guard on the result in the outer loop:
  for row in grid
    if match <- searchRow(row, target)
      result: match
The outer loop naturally stops assigning once result is set, and the inner function handles one level of nesting.

PATTERN 2: FLATTEN AND STREAM
For simple search across nested data, flatten with concatenation and use filter + head:
  allItems <- cat row1 + row2 + row3 | filter by isTarget | head 1 | collect as List of String
This avoids nested loops entirely by working with a flat pipeline.

PATTERN 3: WHILE WITH GUARD
Use a flag to control the outer loop:
  located <- Boolean()
  rowIndex <- 0
  while rowIndex < length grid and ~located?
    row <- grid.getOrDefault(rowIndex, List() of String)
    for item in row
      if item == target
        located: true
    rowIndex: rowIndex + 1
The while loop exits when located becomes set.

KEY PRINCIPLE
If you need nested break, your function is doing too much. Decompose it. Each function handles one level of iteration.

See Q144 for why break was removed. See Q145 for basic break replacement. See Q146 for function decomposition.

defines module qa.without.nestedloop

  defines function

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

    isMatch() as pure
      -> item as String
      <- matched as Boolean: item == "cherry"

  defines program

    NestedLoopDemo()
      stdout <- Stdout()

      // === PATTERN 1: DECOMPOSE INTO FUNCTIONS ===

      row1 <- ["apple", "banana", "cherry"]
      row2 <- ["date", "elderberry", "fig"]
      row3 <- ["grape", "honeydew", "kiwi"]
      grid <- [row1, row2, row3]

      target <- "cherry"
      result <- String()
      for row in grid
        if match <- searchRow(row, target)
          result :=? match
      if result?
        stdout.println(`Found by decomposition: ${result}`)
      else
        stdout.println("Not found by decomposition")

      // === PATTERN 2: FLATTEN AND STREAM ===

      allItems <- row1 + row2 + row3
      streamResult <- cat allItems | filter by isMatch | head 1 | collect as List of String
      stdout.println(`Found by stream: ${streamResult}`)

      // === PATTERN 3: WHILE WITH GUARD ===

      located <- Boolean()
      rowIndex <- 0
      while rowIndex < length grid and ~located?
        row <- grid.getOrDefault(rowIndex, List() of String)
        for item in row
          if item == target
            located: true
        rowIndex: rowIndex + 1
      stdout.println(`Located by while: ${located?}`)

      // === SEARCH FOR MISSING ITEM ===

      missing <- String()
      for row in grid
        if match <- searchRow(row, "mango")
          missing :=? match
      if ~missing?
        stdout.println("Mango not found as expected")

Java: break with label (break outer;) for nested loops. Python: flag variables or raise/except hack. Rust: break with loop labels ('outer: loop { break 'outer; }). Go: break with label, or goto. C/C++: goto or flag variables. Kotlin: break with label (@outer). JavaScript: break with label. EK9: decompose inner loop into a function, guard on result, or flatten and stream.

alternative break decompose exit flatten function grid inner label loop matrix migrate nested no-break no-return outer search

exit nested loop
nested loop
break alternative

Explanation: EK9 has no break statement. To exit a nested loop, decompose into functions and use guard assignment (:=?) so the first successful result wins. See ek9 -h E01070 for details.


Control Flow Without break/continue/return

Question: How do I implement retry logic without break?

How do I retry an operation up to N times without break in EK9?

What replaces while(true) with break for retry loops in EK9?

How do I write a bounded retry loop in EK9?

Answer:
Every language uses while(true) { try... break } for retries. EK9 has no break AND no while(true). Use a counter-bounded for-range loop with a guard on success.

THE PROBLEM
In Java you write: while (true) { result = tryOp(); if (result != null) break; retries++; if (retries >= max) break; }. This has two break conditions, an infinite loop, and requires careful counting.

EK9 PATTERN: FOR-RANGE WITH GUARD
Use a for-range loop bounded by max attempts and guard on the result:
  retryOperation()
    -> maxRetries as Integer
    <- result as String: String()
    for attempt in 1 ... maxRetries
      if ~result?
        result :=? tryOnce(attempt)
The for-range is bounded by design (no infinite loop). The guard on ~result? skips further attempts once a result is set. The :=? ensures only the first successful result is kept.

WHY BETTER THAN BREAK
The loop is bounded by construction. There is no infinite loop risk. The intent is clear: try up to N times, stop on first success. The ~result? check at the top of each iteration replaces the break-on-success pattern.

SIMPLIFIED PATTERN
For simpler cases where the operation returns a set or unset value:
  for attempt in 1 ... maxRetries
    if ~result?
      outcome <- tryOnce(attempt)
      result :=? outcome
The :=? assigns only if result is still unset, so the first success wins.

See Q134 for try-catch patterns. See Q144 for why break was removed. See Q64 for for-range loops.

defines module qa.without.retry

  defines function

    tryOnce() as pure
      -> attempt as Integer
      <- outcome as String: String()
      successAttempt <- 3
      if attempt == successAttempt
        outcome: "success on attempt 3"

    tryAlwaysFails() as pure
      -> attempt as Integer
      <- outcome as String: String()
      require attempt?

    retryOperation()
      -> maxRetries as Integer
      <- result as String: String()
      for attempt in 1 ... maxRetries
        if ~result?
          outcome <- tryOnce(attempt)
          result :=? outcome

    retryExhausted()
      -> maxRetries as Integer
      <- result as String: String()
      for attempt in 1 ... maxRetries
        if ~result?
          outcome <- tryAlwaysFails(attempt)
          result :=? outcome

  defines program

    RetryDemo()
      stdout <- Stdout()

      // === RETRY WITH SUCCESS ON 3RD ATTEMPT ===

      successResult <- retryOperation(5)
      if successResult?
        stdout.println(`Retry succeeded: ${successResult}`)
      else
        stdout.println("Retry failed")

      // === RETRY WITH ALL ATTEMPTS FAILING ===

      failResult <- retryExhausted(5)
      if failResult?
        stdout.println(`Unexpected success: ${failResult}`)
      else
        stdout.println("All 5 attempts failed as expected")

      // === INLINE RETRY PATTERN ===

      inlineResult <- String()
      for attempt in 1 ... 4
        if ~inlineResult?
          minSuccessAttempt <- 2
          if attempt >= minSuccessAttempt
            inlineResult :=? "inline success"
      stdout.println(`Inline retry: ${inlineResult}`)

Java: while(true) { try { result = op(); break; } catch { retries++; if (retries >= max) break; } }. Python: while True: try: result = op(); break; except: retries += 1. Rust: loop { match try_op() { Ok(r) => break r, Err(_) => retries += 1 } }. Go: for retries := 0; retries < max; retries++ { result, err := op(); if err == nil { break } }. EK9: for-range with guard on result, :=? for first-success-wins, no infinite loops possible.

alternative attempt bounded fail guard isset loop maximum migrate no-break no-return null-safe repeat resilient retry safe success tries

Explanation: EK9 has no break statement. For retry logic, use a for-range bounded loop with guard assignment (:=?) so the first success wins without needing break. See ek9 -h E01070 for details.