Question: How do I parse a JSON string in EK9?

How do I create JSON from a string in EK9?

Answer:
Pass a JSON string to the JSON constructor to parse it. Access properties with .get(key) for objects and .get(index) for arrays. Iterate with .iterator().

PARSING JSON STRINGS
Pass a JSON-formatted string to the JSON constructor:
  data <- JSON(`{"name": "Alice", "age": 30}`)

ACCESSING PROPERTIES
Use .get(key) to access object properties:
  name <- parsed.get("name")
Use .get(index) to access array elements:
  first <- jsonArray.get(0)

NATURE CHECKS
Check what kind of JSON value you have:
  parsed.objectNature()  true for JSON objects
  parsed.arrayNature()   true for JSON arrays
  parsed.valueNature()   true for primitives

ITERATION
Iterate over JSON arrays or object entries:
  for item in parsed.iterator()
    stdout.println($item)

See Q188 for JSON as a first-class type. See Q48 for Result type. See Q29 for unset variables.

defines module qa.jsonparsed.parsing

  defines program

    JsonParsingDemo()
      stdout <- Stdout()

      // === PARSING JSON STRINGS ===

      parsed <- JSON(`{"name": "Alice", "age": 30}`)
      stdout.println(`Parsed JSON: ${parsed}`)

      // === ACCESSING PROPERTIES ===

      nameJson <- parsed.get("name")
      stdout.println(`Name: ${nameJson}`)

      ageJson <- parsed.get("age")
      stdout.println(`Age: ${ageJson}`)

      // === NATURE CHECKS ===

      stdout.println(`Is object: ${parsed.objectNature()}`)
      stdout.println(`Is value: ${parsed.valueNature()}`)

      // === JSON ARRAYS ===

      arr <- JSON(`[10, 20, 30]`)
      stdout.println(`Array: ${arr}`)
      stdout.println(`Is array: ${arr.arrayNature()}`)

      firstItem <- arr.get(0)
      stdout.println(`First item: ${firstItem}`)

      // === ITERATION ===

      for item in arr.iterator()
        stdout.println(`Item: ${item}`)

Java: Jackson ObjectMapper.readTree() for tree model, readValue() for POJO binding. Python: json.loads() to dict. Rust: serde_json::from_str(). Go: json.Unmarshal(). Kotlin: kotlinx.serialization Json.decodeFromString(). EK9: JSON constructor parses strings directly, .get() for property access, .iterator() for traversal.

access data error get guard iterate json null ok parse property result serialize string

Explanation: The JSON type does not have 'getString()', 'getInt()', or similar typed accessor methods. Use '.get(key)' for object properties and '.get(index)' for array elements. The result is always a JSON value. See ek9 -h E50060 for details.


Question: How do I process JSON data with stream pipelines in EK9?

How do I extract typed values from JSON using streams?

How do I sum JSON values with pipelines in EK9?

How does collect as work with JSON in EK9?

Answer:
EK9 stream pipelines can extract typed values from JSON using 'collect as Type'. JSON values flow through pipelines and convert to target types automatically.

JSON TO TYPED VALUE
Extract a single typed value from JSON:
  json <- List(JSON("val", JSON(2)))
  anInt <- cat json | collect as Integer
The pipeline extracts the integer value 2.

JSON ARRAY SUMMATION
Convert a list to JSON and sum via pipeline:
  total <- cat $$ [4, 6, 18, 22] | collect as Integer
The $$ converts the list to JSON, pipeline sums as Integer.

JSON OBJECT PROPERTY SUMMATION
Parse a JSON object and sum numeric properties:
  obj <- JSON(`{"a": 1, "b": null, "c": 22}`)
  total <- cat obj | collect as Integer
Null values are skipped automatically (become unset in tri-state model).

NULL HANDLING
JSON null becomes an unset value in EK9:
  json <- List(JSON("val", JSON("null")))
  result <- cat json | collect as Integer
  // result is unset (not result?)

See Q188 for JSON basics. See Q59 for function pipelines. See Q122 for collect as.

defines module qa.jsondata.streams

  defines program

    JsonStreamsDemo()
      stdout <- Stdout()

      // === JSON TO TYPED VALUE ===

      json <- List(JSON("mainValue", JSON(2)))
      anInt <- cat json | collect as Integer
      stdout.println(`Extracted integer: ${anInt}`)

      // === JSON ARRAY SUMMATION ===

      sumOfArray <- cat $$ [4, 6, 18, 22] | collect as Integer
      stdout.println(`Sum of array: ${sumOfArray}`)

      // === JSON OBJECT PROPERTY SUMMATION ===

      jsonObject <- JSON(`{"first": 1, "second": null, "third": 22}`)
      sumOfProps <- cat jsonObject | collect as Integer
      stdout.println(`Sum of properties: ${sumOfProps}`)

      // === NULL HANDLING ===

      // JSON null becomes unset in EK9's tri-state model
      jsonWithNull <- List(JSON("mainValue", JSON("null")))
      nullResult <- cat jsonWithNull | collect as Integer
      if not nullResult?
        stdout.println("JSON null became unset Integer")

      // === FLOAT EXTRACTION ===

      jsonFloat <- List(JSON(2.88))
      aFloat <- cat jsonFloat | collect as Float
      stdout.println(`Extracted float: ${aFloat}`)

Java: Jackson + Stream API for JSON processing, manual type extraction. Python: json.loads() + list comprehensions. Rust: serde_json + iterators. Go: json.Unmarshal to struct + range loops. EK9: JSON integrates with stream pipelines natively, 'collect as Type' extracts and aggregates typed values from JSON.

aggregate collect convert data extract json pipeline serialize stream sum type

jsonObject <- JSON.parse(`{"first": 1, "second": null, "third": 22}`)

Explanation: The JSON type does not have a static 'parse()' method. Pass the JSON string directly to the JSON constructor. The constructor handles parsing automatically. See ek9 -h E50001 for details.


Question: How do I modify and combine JSON objects in EK9?

How do I build a JSON object from parts in EK9?

Answer:
EK9 provides operators for building and combining JSON objects. Use constructors to create objects and operators to merge, replace, or add properties.

BUILDING JSON OBJECTS
Create an empty JSON object:
  obj <- JSON().object()
Create with key-value pairs:
  person <- JSON("name", JSON("Alice"))

ADDING PROPERTIES (+)
Add a key-value pair to a JSON object:
  person + JSON("age", JSON(30))

MERGE OPERATOR (:~:)
Merge properties from one JSON object into another:
  base :~: extras
Existing properties in base are kept, new properties from extras are added.

REPLACE OPERATOR (:^:)
Replace matching properties:
  target :^: replacement
Properties in target that also exist in replacement are overwritten.

See Q188 for JSON basics. See Q96 for operator semantics. See Q189 for converting to JSON.

defines module qa.jsondata.manipulate

  defines program

    JsonManipulateDemo()
      stdout <- Stdout()

      // === BUILDING JSON OBJECTS ===

      emptyObj <- JSON().object()
      stdout.println(`Empty object: ${emptyObj}`)

      person <- JSON("name", JSON("Alice"))
      stdout.println(`Person: ${person}`)

      // === ADDING PROPERTIES WITH + ===

      updated <- person + JSON("age", JSON(30))
      stdout.println(`With age: ${updated}`)

      // === MERGE OPERATOR :~: ===

      base <- JSON(`{"name": "Alice", "role": "dev"}`)
      extras <- JSON(`{"team": "backend", "level": 5}`)
      base :~: extras
      stdout.println(`After merge: ${base}`)

      // === REPLACE OPERATOR :^: ===

      target <- JSON(`{"name": "Alice", "age": 30}`)
      replacement <- JSON(`{"age": 31, "city": "London"}`)
      target :^: replacement
      stdout.println(`After replace: ${target}`)

Java: Jackson ObjectNode.put()/set() for building, JsonNode merge via custom code. Python: dict.update() or {**a, **b} merge. Rust: serde_json::Map insert/extend. Go: manual map merging. Kotlin: mutableMapOf + putAll. EK9: :~: merge and :^: replace operators on JSON, + for adding properties.

add build combine data json manipulate merge object property replace serialize

Explanation: The JSON type does not have a static 'createObject()' method. Create an empty JSON object by calling 'JSON().object()' which creates an unset JSON and then converts it to an empty object. See ek9 -h E50001 for details.


Question: How do I format JSON output in EK9?

How do I get readable JSON output in EK9?

What is the difference between compact and formatted JSON in EK9?

Answer:
EK9 provides both compact and formatted JSON output. Use the $ operator for compact output and .prettyPrint() for human-readable formatted output.

COMPACT OUTPUT ($)
The $ string operator produces compact JSON:
  text <- $jsonObj
No extra whitespace, suitable for APIs and storage.

PRETTY PRINT
The .prettyPrint() method produces indented, readable JSON:
  formatted <- jsonObj.prettyPrint()
Useful for debugging, logging, and configuration files.

STRING INTERPOLATION
Use JSON values directly in string interpolation:
  stdout.println(`Data: ${jsonObj}`)
This calls the $ operator automatically.

See Q188 for JSON as a first-class type. See Q43 for string interpolation.

defines module qa.jsondata.output

  defines program

    JsonOutputDemo()
      stdout <- Stdout()

      jsonData <- JSON(`{"name": "Alice", "age": 30, "active": true}`)

      // === COMPACT OUTPUT WITH $ ===

      compact <- $jsonData
      stdout.println("Compact: " + compact)

      // === PRETTY PRINT ===

      formatted <- jsonData.prettyPrint()
      stdout.println("Formatted:")
      stdout.println(formatted)

      // === STRING INTERPOLATION ===

      stdout.println(`Interpolated: ${jsonData}`)

      // === NESTED JSON ===

      nested <- JSON(`{"person": {"name": "Bob", "scores": [90, 85, 92]}}`)
      stdout.println("Nested compact: " + $nested)
      stdout.println("Nested formatted:")
      stdout.println(nested.prettyPrint())

Java: Jackson ObjectMapper.writerWithDefaultPrettyPrinter(). Python: json.dumps(indent=2). Rust: serde_json::to_string_pretty(). Go: json.MarshalIndent(). Kotlin: Json { prettyPrint = true }. EK9: .prettyPrint() method on JSON type, $ operator for compact form.

compact data display format json output pretty print readable serialize string

Explanation: The JSON type does not have a 'toString()' method. Use the '$' operator for compact string conversion and '.prettyPrint()' for formatted output. EK9 uses operator syntax, not Java-style method calls. See ek9 -h E50060 for details.


Question: How do I define a generic class in EK9?

How do I create a parameterized class in EK9?

Answer:
EK9 uses natural-language syntax for generics with 'of type T'. Generic classes define type parameters that are filled in when the class is instantiated.

DEFINING A GENERIC CLASS
Use 'of type T' after the class name:
  Container of type T
    item as T?
T is the type parameter that gets replaced with a real type.

CONSTRUCTOR WITH TYPE PARAMETER
Constructors accept T-typed arguments:
  Container()
    -> val as T
    item :=? val

METHODS RETURNING T
Methods can return the type parameter:
  get()
    <- rtn as T: T()

INSTANTIATION WITH TYPE INFERENCE
The type parameter is inferred from the constructor argument:
  strContainer <- Container("hello")
Or explicitly specified:
  intContainer <- Container() of Integer

See Q58 for generic functions. See Q93 for class basics. See Q101 for closed by default. See Q195 for type constraints. See Q196 for multi-parameter generics. See Q197 for extending generics. See Q198 for built-in generics.

See Q642 for generic constructor inference. See Q653 for type independence.

defines module qa.generics.classes

  defines class

    Container of type T
      item as T?

      Container() as pure
        item :=? T()

      Container() as pure
        -> initialItem as T
        item :=? initialItem

      get()
        <- rtn as T: T()
        rtn :=? T(item)

      default operator ?

  defines program

    GenericClassDemo()
      stdout <- Stdout()

      // === INSTANTIATION WITH TYPE INFERENCE ===

      strContainer <- Container("hello")
      stdout.println(`String container set: ${strContainer?}`)

      // === EXPLICIT TYPE SPECIFICATION ===

      intContainer <- Container(42)
      stdout.println(`Integer container set: ${intContainer?}`)

      // === DEFAULT CONSTRUCTION ===

      emptyContainer <- Container() of String
      stdout.println(`Empty container set: ${emptyContainer?}`)

      // === ACCESS VALUE ===

      retrieved <- strContainer.get()
      if retrieved?
        stdout.println(`Retrieved: ${retrieved}`)

Java: class Container<T> with angle brackets, type erasure at runtime. C++: template<typename T> with full monomorphization. Rust: struct Container<T> with trait bounds. Go: type Container[T any] (Go 1.18+). Kotlin: class Container<T> similar to Java. EK9: 'Container of type T' reads as natural English, type inference from constructors.

class container define generic inference instantiate parameter parameterized type type-parameter

generic class
generics
type parameter
template

emptyContainer <- Container() of (String, Integer)

Explanation: Container has one type parameter T, so providing two type arguments is wrong. The number of type arguments must match the number of type parameters. See ek9 -h E06020 for details.

Oracle can generate a generic class outline with type parameters and constrained methods.


Question: How do I constrain generic type parameters in EK9?

How do I restrict what types can be used with a generic in EK9?

How do I add type bounds to generics in EK9?

Answer:
EK9 uses 'constrain by' to restrict generic type parameters to subtypes of a specific class. This enables safe access to methods of the constraining type.

CONSTRAIN BY SYNTAX
Restrict T to subtypes of a base type:
  Handler of type T constrain by Animal
Now T is guaranteed to have all Animal methods.

ACCESSING CONSTRAINED METHODS
Inside the generic, T's methods from Animal are accessible:
  process()
    -> item as T
    name <- item.name()
Because T must be an Animal, calling name() is safe.

COMPILE-TIME SAFETY
Using an incompatible type is a compile error:
  Handler of String  // ERROR: String is not an Animal
Only Animal subtypes are accepted.

FUNCTION CONSTRAINTS
Generic functions also support constraints:
  describe() of type T constrain by Animal
    -> item as T
    <- rtn as String: item.name()

See Q194 for generic classes. See Q58 for generic functions. See Q103 for abstract classes. See Q257 for constrained types (constrain as vs constrain by).

defines module qa.generics.constraints

  defines class

    Animal as abstract
      animalName as String?

      default private Animal() as pure

      Animal() as pure
        -> n as String
        animalName :=? String(n)

      name() as pure
        <- rtn as String: String(animalName)

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

    Dog is Animal

      Dog() as pure
        -> n as String
        super(n)

      Dog() as pure
        super("Rex")

      bark() as pure
        <- rtn as String: "Woof!"

    Cat is Animal

      Cat() as pure
        -> n as String
        super(n)

      Cat() as pure
        super("Whiskers")

      purr() as pure
        <- rtn as String: "Purr..."

  defines function

    // Generic function constrained to Animal subtypes
    describeAnimal() of type T constrain by Animal as open
      -> item as T
      <- rtn as String: item.name()

  defines program

    GenericConstraintsDemo()
      stdout <- Stdout()

      // === CONSTRAINED GENERIC FUNCTION ===

      dog <- Dog()
      dogDesc <- describeAnimal(dog)
      stdout.println(`Dog: ${dogDesc}`)

      theCat <- Cat()
      catDesc <- describeAnimal(theCat)
      stdout.println(`Cat: ${catDesc}`)

      // The following would NOT compile:
      // describeAnimal("hello")  // String is not an Animal

      stdout.println("Type constraints ensure only Animal subtypes are accepted")

Java: <T extends Animal> for upper bounds. C#: where T : Animal. Rust: <T: Animal> trait bounds. Go: type T Animal constraint (Go 1.18+). Kotlin: <T : Animal> bounds. EK9: 'of type T constrain by Animal' reads as natural English, same syntax for functions and classes.

bound constant constrain constraint extends generic parameterized restrict safety subtype type type-parameter

generic constraint
type constraint
bounded type

Explanation: The method 'describe()' does not exist on Dog. Use the generic function 'describeAnimal()' which works on any Animal subtype. See ek9 -h E50060 for details.


Question: How do I define generics with multiple type parameters in EK9?

Can EK9 generics have more than one type parameter?

How do I create a Pair type with two generic parameters?

What is the syntax for multi-parameter generics in EK9?

Answer:
EK9 supports multiple type parameters using parentheses: 'of type (A, B)'. This enables types like Pair, Either, and mapper functions with distinct input/output types.

MULTIPLE TYPE PARAMETERS
Use parentheses for multiple parameters:
  Pair of type (A, B)
    first as A?
    second as B?

INSTANTIATION
Provide both types explicitly:
  pair <- Pair("hello", 42) of (String, Integer)
Or let EK9 infer them:
  pair <- Pair("hello", 42)

GENERIC FUNCTIONS WITH MULTIPLE PARAMETERS
Functions can also use multiple type parameters:
  mapper() of type (S, T) as open
    -> source as S
    <- target as T?

BUILT-IN EXAMPLES
Dict, Result, and DictEntry all use multiple type parameters:
  Dict of (String, Integer)
  Result of (String, Integer)

See Q194 for single-parameter generics. See Q195 for constraints. See Q46 for Dict.

See Q654 for parameter count validation. See Q655 for method overloading with generics.

defines module qa.generics.multitype

  defines class

    Pair of type (A, B)
      first as A?
      second as B?

      Pair() as pure
        first :=? A()
        second :=? B()

      Pair() as pure
        ->
          a as A
          b as B
        first :=? a
        second :=? b

      getFirst()
        <- rtn as A: A()
        rtn :=? A(first)

      getSecond()
        <- rtn as B: B()
        rtn :=? B(second)

      default operator ?

  defines program

    MultiParamGenericDemo()
      stdout <- Stdout()

      // === INSTANTIATION WITH INFERENCE ===

      pair <- Pair("hello", 42)
      stdout.println(`Pair is set: ${pair?}`)

      // === ACCESSING VALUES ===

      first <- pair.getFirst()
      if first?
        stdout.println(`First: ${first}`)

      second <- pair.getSecond()
      if second?
        stdout.println(`Second: ${second}`)

      // === DIFFERENT TYPE COMBINATIONS ===

      boolFloat <- Pair(true, 3.14)
      stdout.println(`Bool-Float pair set: ${boolFloat?}`)

Java: class Pair<A, B> with angle brackets. C++: template<typename A, typename B>. Rust: struct Pair<A, B>. Go: type Pair[A, B any]. Kotlin: class Pair<A, B>. EK9: 'Pair of type (A, B)' with parentheses for multiple parameters, reads as natural English.

define generic multi multiple pair parameter parameterized parentheses tuple type type-parameter

Explanation: Pair requires two type parameters (A, B). Providing only one type argument does not match the generic definition. See ek9 -h E06220 for details.

Explanation: A generic class constructor parameter count must match the number of type parameters. Pair of type (A, B) needs a constructor accepting both A and B. See ek9 -h E06030 for details.

Oracle can generate a generic class with multiple type parameters.


Question: How do I extend or specialize a generic type in EK9?

How do I create a concrete type from a generic in EK9?

Can I extend a parameterized type in EK9?

How does generic specialization work in EK9?

Answer:
EK9 allows extending generic types that are marked 'as open'. You create concrete specializations by providing specific type arguments.

OPEN GENERIC TYPES
Mark a generic type as open to allow extension:
  BaseList of type T as open
    items as List of T?

SPECIALIZATION VIA EXTENSION
Create a concrete type by extending with specific types:
  NameList is BaseList of String
NameList is now a concrete class with String-typed items.

ADDING METHODS
Specializations can add new methods:
  NameList is BaseList of String
    findByPrefix()
      -> prefix as String
      <- rtn as String?

GENERIC FUNCTION INSTANTIATION
Generic functions use 'is' or 'extends' for instantiation:
  intCompare <- () is compareFunc of Integer as function

See Q194 for generic classes. See Q101 for closed by default. See Q102 for as open. See Q58 for generic functions.

defines module qa.generics.extending

  defines function

    compareFunc() of type T as open
      ->
        item1 as T
        item2 as T
      <-
        rtn as Integer := item1 <=> item2

  defines class

    BaseList of type T as open
      items as List of T?

      BaseList()
        items :=? List() of T

      BaseList()
        -> item as T
        items: List() of T
        items += item

      addItem()
        -> item as T
        if not items?
          items: List() of T
        items += item

      count() as pure
        <- rtn as Integer: 0
        if items?
          rtn: length items

      default operator ?

    // Specialization: concrete type from generic
    NameList is BaseList of String

      default operator ?

  defines program

    ExtendingGenericsDemo()
      stdout <- Stdout()

      // === GENERIC FUNCTION INSTANTIATION ===

      intCompare <- () is compareFunc of Integer as function
      result <- intCompare(10, 20)
      stdout.println(`Compare 10 vs 20: ${result}`)

      strCompare <- () extends compareFunc of String as function
      strResult <- strCompare("apple", "banana")
      stdout.println(`Compare apple vs banana: ${strResult}`)

      // === SPECIALIZED CLASS ===

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

Java: class NameList extends ArrayList<String>. C++: template specialization or typedef. Rust: type alias or newtype pattern. Go: type NameList = List[string] (no true specialization). Kotlin: class NameList : ArrayList<String>(). EK9: 'NameList is BaseList of String' with optional additional methods.

concrete extend generic inherit open parameterize parameterized specialize type type-parameter

intCompare <- () is compareFunc of Integer as class

Explanation: Generic functions must be instantiated with 'as function', not 'as class'. Using the wrong construct type triggers E50020 — type not resolved. See ek9 -h E50020 for details.


Question: What built-in generic types does EK9 provide?

Can I extend built-in generic types in EK9?

Answer:
EK9 provides several built-in generic types. These are closed by default and cannot be extended. Use composition (wrapping) instead of inheritance.

LIST OF T
Ordered collection:
  names <- List() of String
  names += "Alice"

DICT OF (K, V)
Key-value mapping:
  ages <- Dict() of (String, Integer)

OPTIONAL OF T
May or may not contain a value:
  maybe <- Optional("hello")

RESULT OF (OK, ERR)
Success or error value:
  result <- Result(42) of (Integer, String)

ITERATOR OF T
Lazy sequence of values from .iterator() on collections.

MUTEXLOCK OF T
Thread-safe wrapper for concurrent access. Held as a FIELD on a wrapping class (locals are rejected by E08256). See Q158 for the canonical field-on-class pattern.

CLOSED BY DEFAULT
Built-in generics cannot be extended. Use composition:
  MyList             // CORRECT: wrap a List
    items as List of String

See Q45 for List. See Q46 for Dict. See Q47 for Optional. See Q48 for Result. See Q128 for closed collections. See Q158 for MutexLock.

defines module qa.generics.builtin

  defines program

    BuiltinGenericsDemo()
      stdout <- Stdout()

      // === LIST OF T ===

      names <- List() of String
      names += "Alice"
      names += "Bob"
      stdout.println(`List size: ${length names}`)

      // === DICT OF (K, V) ===

      ages <- {"Alice": 30, "Bob": 25}
      stdout.println(`Dict size: ${length ages}`)

      // === OPTIONAL OF T ===

      maybe <- Optional("hello")
      if maybe?
        stdout.println(`Optional has value: ${maybe}`)

      emptyOpt <- Optional() of String
      stdout.println(`Empty optional set: ${emptyOpt?}`)

      // === RESULT OF (OK, ERR) ===

      okResult <- Result("Success", Integer())
      stdout.println(`Result is ok: ${okResult?}`)

      // === MUTEXLOCK OF T ===
      // MutexLock must be a FIELD on a wrapping class — see Q158 and Q213
      // for the canonical pattern. Locals are rejected (E08256).

      stdout.println("MutexLock of T — see Q158 for the field-on-class pattern")

      // === CANNOT EXTEND (CLOSED) ===
      // MyList extends List of String  // ERROR: not open
      // Use composition instead:
      //   MyList
      //     items as List of String

      stdout.println("Built-in generics are closed, use composition")

Java: ArrayList<T>, HashMap<K,V>, Optional<T> are all open to extension. Python: list, dict are open. Rust: Vec<T>, HashMap<K,V> cannot be inherited (no inheritance). Go: slices and maps are built-in, no generics before 1.18. Kotlin: List<T>, Map<K,V> are interfaces. EK9: List, Dict, Optional, Result, MutexLock are closed generic types, use composition pattern.

absent built-in dict error generic guard iterator list mutexlock ok optional parameterized priorityqueue result safe type-parameter

Explanation: List takes one type parameter T. Providing two type arguments does not match the generic definition. See ek9 -h E06020 for details.


Question: How do I create a REST GET endpoint in EK9?

How do I build a web API endpoint in EK9?

Answer:
EK9 has a built-in 'defines service' construct for REST endpoints. Services declare URI paths and methods that map to HTTP operations.

SERVICE DEFINITION
Define a service with a base URI path:
  defines service
    Info :/info
The service name 'Info' is bound to the '/info' path.

GET METHOD
Named methods default to GET:
  welcome() as GET for :/welcome
    <- response as HTTPResponse: ...

HTTPRESPONSE TRAIT
Return an HTTPResponse from service methods. Use a dynamic class with trait delegation:
  () with trait HTTPResponse
    override content()
      <- rtn as String: "Hello"
    override status() as pure
      <- rtn as Integer: 200

APPLICATION REGISTRATION
Register services in an application:
  defines application
    MyApp
      register Info()

See Q200 for CRUD operators. See Q201 for HTTP responses. See Q112 for full service example. See Q111 for components.

See Q657 for URI mapping. See Q659 for HTTPResponse.

defines module qa.web.restget

  defines text for "en"

    WelcomeText
      greeting()
        "Welcome to the EK9 Service"

  defines service

    Info :/info open

      welcome() as GET for :/welcome
        <- response as HTTPResponse: () with trait HTTPResponse
          text <- WelcomeText("en")

          override cacheControl() as pure
            <- rtn as String: "no-store"
          override contentType() as pure
            <- rtn as String: "text/plain"
          override contentLanguage() as pure
            <- rtn as String: "en"
          override content()
            <- rtn as String: text.greeting()
          override status() as pure
            <- rtn as Integer: 200
          default operator ?

  defines application

    WebApp
      register Info()

  defines program

    RestGetDemo()
      stdout <- Stdout()

      stdout.println("Service registered with application")
      stdout.println("GET /info/welcome returns welcome text")
      stdout.println("HTTPResponse provides status, content, headers")

Java: JAX-RS @GET @Path or Spring @GetMapping. Python: Flask @app.route('/path'). Rust: actix-web or axum handler functions. Go: http.HandleFunc('/path', handler). Kotlin: Ktor routing DSL. EK9: language-level 'defines service' with :/path URIs, named methods default to GET.

api define endpoint get http path response rest service uri

REST
REST endpoint
GET endpoint
HTTP GET

Explanation: The WelcomeText text construct expects a String locale parameter. Passing an Integer triggers E50060 — constructor not resolved. See ek9 -h E50060 for details.

Oracle can generate a service with REST endpoint methods and path bindings.


Question: How do EK9 operators map to HTTP methods for CRUD?

How do I create POST, PUT, DELETE endpoints in EK9?

What operators map to HTTP methods in EK9 services?

How does EK9 map CRUD to service operators?

Answer:
EK9 maps operators to HTTP methods semantically. This makes CRUD operations natural and consistent with the language's operator design.

OPERATOR TO HTTP MAPPING
Named methods and operators map to HTTP verbs:
  listAll() :/             GET (read)
  operator += :/           POST (create/add)
  operator -= :/{id}       DELETE (remove)
  operator :~: :/{id}      PATCH (merge/partial update)
  operator :^: :/{id}      PUT (replace)

WHY OPERATORS
The mapping is semantic, not arbitrary:
  += adds to a collection (POST creates a resource)
  -= removes from a collection (DELETE removes a resource)
  :~: merges into existing (PATCH partially updates)
  :^: replaces entirely (PUT replaces completely)

EACH RETURNS HTTPResponse
All CRUD methods return an HTTPResponse with status, content, and headers.

See Q199 for GET endpoints. See Q201 for HTTP responses. See Q202 for parameter binding. See Q96 for operator semantics.

See Q660 for CRUD operator patterns. See Q658 for path parameters.

defines module qa.web.crud

  defines service

    Items :/items open

      // GET /items — list all items
      listAll() :/
        <- response as HTTPResponse: () with trait HTTPResponse
          override content()
            <- rtn as String: `["item1", "item2"]`
          override status() as pure
            <- rtn as Integer: 200
          override contentType() as pure
            <- rtn as String: "application/json"
          override cacheControl() as pure
            <- rtn as String: "no-cache"
          override contentLanguage() as pure
            <- rtn as String: "en"
          default operator ?

      // POST /items — add new item
      operator += :/
        -> content as String :=: CONTENT
        <- response as HTTPResponse: () with trait HTTPResponse
          override content()
            <- rtn as String: `{"created": true}`
          override status() as pure
            <- rtn as Integer: 201
          override contentType() as pure
            <- rtn as String: "application/json"
          override cacheControl() as pure
            <- rtn as String: "no-store"
          override contentLanguage() as pure
            <- rtn as String: "en"
          default operator ?

      // DELETE /items/{id} — remove item
      operator -= :/{id}
        -> id as String
        <- response as HTTPResponse: () with trait HTTPResponse
          override content()
            <- rtn as String: ""
          override status() as pure
            <- rtn as Integer: 204
          override contentType() as pure
            <- rtn as String: "text/plain"
          override cacheControl() as pure
            <- rtn as String: "no-store"
          override contentLanguage() as pure
            <- rtn as String: "en"
          default operator ?

  defines application

    CrudApp
      register Items()

  defines program

    CrudOperatorsDemo()
      stdout <- Stdout()

      stdout.println("CRUD operator mapping:")
      stdout.println("  += maps to POST (create)")
      stdout.println("  -= maps to DELETE (remove)")
      stdout.println("  :~: maps to PATCH (merge)")
      stdout.println("  :^: maps to PUT (replace)")

Java: JAX-RS @POST/@PUT/@DELETE annotations. Python: Flask methods=['POST','PUT','DELETE']. Rust: actix-web .post()/.put()/.delete(). Go: manual method checking in handler. Kotlin: Ktor post/put/delete route blocks. EK9: operators (+=, -=, :~:, :^:) map semantically to POST, DELETE, PATCH, PUT.

crud delete dict http mapping method operator patch post put rest service

Explanation: Stdout does not have a display() method. The correct method is println(). Calling a non-existent method triggers E50060 — method not resolved. See ek9 -h E50060 for details.


Question: How do I create HTTP responses in EK9 services?

How does the HTTPResponse trait work in EK9?

How do I set HTTP status codes in EK9 services?

How do I return JSON from an EK9 service?

Answer:
Service methods return HTTPResponse, a trait with methods for content, status, headers, and caching. Use dynamic classes to implement it inline.

HTTPRESPONSE TRAIT
HTTPResponse defines these methods:
  content()           the response body
  status()            HTTP status code (200, 404, etc.)
  contentType()       MIME type (application/json, etc.)
  cacheControl()      cache directives
  contentLanguage()   language tag

DYNAMIC IMPLEMENTATION
Create inline with dynamic class syntax:
  <- response as HTTPResponse: () with trait HTTPResponse
    override content()
      <- rtn as String: `{"msg": "ok"}`
    override status() as pure
      <- rtn as Integer: 200
    override contentType() as pure
      <- rtn as String: "application/json"
    ...
    default operator ?

STATUS CODES
Return appropriate HTTP status codes:
  200 for success, 201 for created,
  204 for no content, 404 for not found.

See Q199 for REST GET endpoints. See Q200 for CRUD operators. See Q115 for dynamic classes. See Q106 for traits.

defines module qa.web.httpresponse

  defines service

    Health :/health open

      check() as GET for :/status
        <- response as HTTPResponse: () with trait HTTPResponse
          override content()
            <- rtn as String: `{"status": "healthy"}`
          override status() as pure
            <- rtn as Integer: 200
          override contentType() as pure
            <- rtn as String: "application/json"
          override cacheControl() as pure
            <- rtn as String: "no-cache"
          override contentLanguage() as pure
            <- rtn as String: "en"
          default operator ?

  defines application

    HealthApp
      register Health()

  defines program

    HttpResponseDemo()
      stdout <- Stdout()

      stdout.println("HTTPResponse trait methods:")
      stdout.println("  content() - response body")
      stdout.println("  status() - HTTP status code")
      stdout.println("  contentType() - MIME type")
      stdout.println("  cacheControl() - cache directive")
      stdout.println("  contentLanguage() - language tag")

Java: JAX-RS Response.ok().entity(body).build() or Spring ResponseEntity. Python: Flask make_response() or return tuple (body, status). Rust: axum IntoResponse trait. Go: w.WriteHeader(status) + w.Write(body). Kotlin: Ktor call.respond(). EK9: dynamic class implementing HTTPResponse trait inline.

anonymous cache capture closure content define delegate dynamic http response rest service status trait

Oracle can generate a service with HTTP response handling methods.


Question: How do I bind path parameters and request body in EK9 services?

How does automatic parameter binding work in EK9 services?

How do I access the request body in EK9?

How does parameter binding work in EK9 services?

Answer:
EK9 services bind path parameters and request content automatically using URI templates and binding keywords.

PATH PARAMETERS (IMPLICIT)
Path segments in braces bind to parameters by name:
  byId() as GET for :/{id}
    -> id as String
The {id} in the path automatically binds to the id parameter.

EXPLICIT PATH BINDING
Use :=: PATH for explicit binding to a named path segment:
  -> itemId as String :=: PATH "item-id"
Binds parameter 'itemId' to the path segment named 'item-id'.

CONTENT BINDING
Access the request body with :=: CONTENT:
  -> content as String :=: CONTENT
The entire request body is bound to the parameter.

REQUEST BINDING
Access the full request object with :=: REQUEST:
  -> request as HTTPRequest :=: REQUEST
Provides access to all request information.

See Q199 for REST GET endpoints. See Q200 for CRUD operators. See Q112 for full service example.

defines module qa.web.parameters

  defines service

    Products :/products open

      // Path parameter implicit binding
      byId() as GET for :/{id}
        -> id as String
        <- response as HTTPResponse?

        itemId <- String(id)
        response: (itemId) with trait HTTPResponse
          override content()
            <- rtn as String: `{"id": "${itemId}"}`
          override status() as pure
            <- rtn as Integer: 200
          override contentType() as pure
            <- rtn as String: "application/json"
          override cacheControl() as pure
            <- rtn as String: "no-cache"
          override contentLanguage() as pure
            <- rtn as String: "en"
          default operator ?

      // Content binding for POST body
      operator += :/
        -> content as String :=: CONTENT
        <- response as HTTPResponse: () with trait HTTPResponse
          override content()
            <- rtn as String: `{"received": true}`
          override status() as pure
            <- rtn as Integer: 201
          override contentType() as pure
            <- rtn as String: "application/json"
          override cacheControl() as pure
            <- rtn as String: "no-store"
          override contentLanguage() as pure
            <- rtn as String: "en"
          default operator ?

  defines application

    ProductApp
      register Products()

  defines program

    ParameterBindingDemo()
      stdout <- Stdout()

      stdout.println("Path parameter binding:")
      stdout.println("  -> id as String (implicit from {id})")
      stdout.println("Content binding:")
      stdout.println("  -> content as String :=: CONTENT")
      stdout.println("Request binding:")
      stdout.println("  -> request as HTTPRequest :=: REQUEST")

Java: JAX-RS @PathParam("id"), @RequestBody. Python: Flask route('<id>') and request.json. Rust: axum Path(id) and Json(body) extractors. Go: mux.Vars(r)["id"] and io.ReadAll(r.Body). Kotlin: Ktor call.parameters["id"] and call.receive(). EK9: {id} in path auto-binds, :=: CONTENT for body, :=: REQUEST for full request.

bind body content extract http parameter path request rest service uri


Question: How do I wire services and components together in an EK9 application?

How does application registration work in EK9?

How do I connect services and components in EK9?

How do I use dependency injection with services in EK9?

Answer:
EK9 applications wire services and components together using 'register' declarations. Programs access injected components with the '!' suffix.

COMPONENT DEFINITION
Define abstract and concrete components:
  defines component
    Repository as abstract
      findAll() as abstract
        <- rtn as List of String?
    InMemoryRepo extends Repository
      override findAll()
        <- rtn <- List() of String

APPLICATION REGISTRATION
Register concrete components and services:
  defines application
    MyApp
      register InMemoryRepo() as Repository
      register ItemService()

PROGRAM WITH APPLICATION
Link a program to an application for injection:
  MyProgram() with application of MyApp
    repo as Repository!
The '!' suffix injects the registered Repository.

WIRING ORDER
The application definition controls which implementations satisfy abstract dependencies. The compiler validates that all injection points can be satisfied.

See Q111 for component basics. See Q112 for service basics. See Q199 for REST endpoints. See Q114 for aspects. See Q227 for compile-time DI validation. See Q228 for registration ordering. See Q231 for program-application linking.

defines module qa.web.application

  defines component

    Repository as abstract
      findAll() as abstract
        <- rtn as List of String?

      default operator ?

    InMemoryRepo extends Repository
      override findAll()
        <- rtn <- List() of String
        rtn += "item1"
        rtn += "item2"

      default operator ?

  defines service

    ItemService :/items open

      listAll() :/
        <- response as HTTPResponse: () with trait HTTPResponse
          override content()
            <- rtn as String: `{"items": []}`
          override status() as pure
            <- rtn as Integer: 200
          override contentType() as pure
            <- rtn as String: "application/json"
          override cacheControl() as pure
            <- rtn as String: "no-cache"
          override contentLanguage() as pure
            <- rtn as String: "en"
          default operator ?

  defines application

    MyApp
      register InMemoryRepo() as Repository
      register ItemService()

  defines program

    AppWiringDemo() with application of MyApp
      stdout <- Stdout()

      repo as Repository!

      items <- repo.findAll()
      for item in items
        stdout.println(`Item: ${item}`)

      stdout.println("Components and services wired via application")

Java: Spring @Configuration with @Bean definitions, @Autowired injection. Python: FastAPI Depends() or manual DI. Rust: no built-in DI. Go: manual wiring or wire codegen. Kotlin: Koin module { single { } } or Dagger @Module. EK9: 'register X() as Y' in application definition, '!' suffix for injection.

application component compose dependency http inject register rest service wire

Explanation: Repository does not have a getAll() method. The correct method name is findAll(). Using the wrong method name triggers E50060 — method not resolved. See ek9 -h E50060 for details.

Oracle can generate an application block with component registrations and service wiring.


Question: How do I write black-box tests with expected output files in EK9?

How does output-based testing work in EK9?

What is the expected_output.txt pattern in EK9?

Answer:
EK9 black-box tests validate program behavior by comparing stdout output against an expected_output.txt companion file. No assertions needed.

BLACK-BOX TEST PATTERN
Mark a program with @Test:
  @Test
  MyTestProgram()
    stdout <- Stdout()
    stdout.println("Expected output")

EXPECTED OUTPUT FILE
Create a companion expected_output.txt in the same directory:
  Expected output
The test runner compares stdout against this file line by line.

NO ASSERTIONS NEEDED
The output IS the test. Print values and let the file comparison verify correctness. This eliminates testing framework boilerplate.

WHEN TO USE BLACK-BOX TESTS
Ideal for: integration tests, output formatting, end-to-end workflows, and any test where verifying printed output is the goal.

See Q205 for parameterized tests. See Q155 for basic unit testing. See Q156 for assertions. See Q208 for dynamic output placeholders.

defines module qa.testdeep.blackbox

  defines program

    // === BLACK-BOX TEST ===
    // A @Test program prints to stdout.
    // A companion expected_output.txt file
    // contains the expected output.
    // The test runner compares them automatically.

    // Note: This file demonstrates the pattern.
    // In a real test directory, you would have:
    //   myTest/myTest.ek9
    //   myTest/expected_output.txt

    BlackBoxTestingDemo()
      stdout <- Stdout()

      stdout.println("Black-box testing pattern:")
      stdout.println("  1. Write a @Test program")
      stdout.println("  2. Print expected values to stdout")
      stdout.println("  3. Create expected_output.txt file")
      stdout.println("  4. Test runner compares output")
      stdout.println("No assertions needed")

Java: JUnit requires assertion methods and output stream capture. Python: pytest capsys fixture for output capture. Rust: custom output capture helpers. Go: testing.T with stdout redirection. EK9: @Test program with expected_output.txt companion file, no assertions or framework API needed.

black-box comparison coverage expected file output stdout test verify

stdout.println("Black-box testing pattern:")
      stdout <- Stdout()

Explanation: Variables must be declared before use. Using stdout before its declaration is a forward reference. See ek9 -h E50010 for details.


Question: How do I write parameterized tests in EK9?

How do I run the same test with different data in EK9?

Answer:
EK9 parameterized tests accept typed parameters via standard program parameter syntax. Test data comes from external files.

PARAMETERIZED TEST PROGRAM
Declare parameters with the -> syntax:
  @Test
  MyTest()
    -> input as String
    stdout <- Stdout()
    stdout.println("Got: " + input)

TEST DATA FILES
Each test case has:
  commandline_arg_<N>.txt  - input parameters
  expected_case_<N>.txt    - expected output
Where N is the case number (1, 2, 3, ...).

TYPED PARAMETERS
Parameters are strongly typed. The compiler validates types at compile time, not at test runtime.

MULTIPLE PARAMETERS
  @Test
  Calculator()
    ->
      a as Integer
      b as Integer
    stdout <- Stdout()
    stdout.println($a + b)

See Q204 for black-box testing. See Q155 for basic testing. See Q2 for compile and run.

defines module qa.testdeep.parameterized

  defines program

    // === PARAMETERIZED TEST PATTERN ===
    // @Test programs accept parameters just like regular programs.
    // Test data comes from external files:
    //   commandline_arg_1.txt, expected_case_1.txt
    //   commandline_arg_2.txt, expected_case_2.txt

    ParameterizedTestDemo()
      stdout <- Stdout()

      stdout.println("Parameterized test pattern:")
      stdout.println("  1. @Test program with -> parameters")
      stdout.println("  2. commandline_arg_N.txt for inputs")
      stdout.println("  3. expected_case_N.txt for outputs")
      stdout.println("  4. Same test logic, different data")
      stdout.println("Parameters are strongly typed")

Java: JUnit @ParameterizedTest with @ValueSource/@CsvSource. Python: @pytest.mark.parametrize. Rust: custom macros or rstest crate. Go: table-driven tests with struct slices. Kotlin: JUnit parameterized or Kotest forAll. EK9: standard program parameters with commandline_arg/expected_case file pairs.

case commandline coverage data-driven expected generic input multiple parameterized test

stdout.println("Parameterized test pattern:")
      stdout <- Stdout()

Explanation: Variables must be declared before use. Using stdout before declaring it with '<-' is a forward reference. See ek9 -h E50010 for details.


Question: How do I measure test coverage in EK9?

How does code coverage work in EK9?
What coverage modes does EK9 support?
How do I get a coverage report in EK9?

Answer:
EK9 has built-in test coverage instrumentation. Use the -tC flag to enable coverage, with three modes and a configurable threshold.

COVERAGE FLAG
Run tests with coverage:
  ek9 -tC myTests.ek9
Or: ek9 -t4 myTests.ek9 (JSON + coverage)

COVERAGE MODES
Three instrumentation modes:
  SET    - records whether each line was executed (default)
  COUNT  - counts how many times each line ran
  ATOMIC - thread-safe counting for concurrent tests

THRESHOLD
The default coverage threshold is 80%. Tests fail if coverage falls below this. Configure in package settings.

OUTPUT FORMATS WITH COVERAGE
  -t4  JSON output with coverage data
  -t5  Verbose coverage breakdown
  -t6  HTML dashboard with visual coverage

See Q207 for all output formats. See Q155 for basic testing. See Q157 for running tests. See Q321 for the quality report dashboard.

defines module qa.testdeep.coverage

  defines function

    addNumbers() as pure
      ->
        a as Integer
        b as Integer
      <-
        rtn as Integer: a + b

    isPositive() as pure
      -> n as Integer
      <- rtn as Boolean: n > 0

  defines program

    CoverageDemo()
      stdout <- Stdout()

      // === FUNCTIONS TO COVER ===

      result <- addNumbers(3, 4)
      stdout.println(`3 + 4 = ${result}`)

      positive <- isPositive(5)
      stdout.println(`5 is positive: ${positive}`)

      negative <- isPositive(-1)
      stdout.println(`-1 is positive: ${negative}`)

      // === COVERAGE MODES ===
      // Run with: ek9 -tC myTests.ek9
      // SET    - line executed yes/no
      // COUNT  - execution count per line
      // ATOMIC - thread-safe counting

      stdout.println("Coverage: SET, COUNT, or ATOMIC modes")
      stdout.println("Default threshold: 80%")
      stdout.println("Formats: -t4 JSON, -t5 verbose, -t6 HTML")

Java: JaCoCo or Cobertura external tools. Python: coverage.py with pytest-cov. Rust: cargo-tarpaulin or llvm-cov. Go: go test -cover built-in. Kotlin: JaCoCo via Gradle. EK9: built-in coverage with SET/COUNT/ATOMIC modes, 80% default threshold, HTML dashboard.

atomic count coverage html instrument percent report set test threshold

isPositive() as pure
      -> n as Integer
      <- rtn as Boolean: true

Explanation: The parameter n is declared but never used in the function body. All parameters must be referenced in the implementation. See ek9 -h E08091 for details.


Question: What test output formats does EK9 support for CI/CD?

How do I get JUnit XML output from EK9 tests?

What test report formats does EK9 support?

How do I integrate EK9 tests with CI pipelines?

Answer:
EK9 supports 7 output formats via the -t flag, from terse to full HTML dashboards with coverage.

OUTPUT FORMATS
  -t0  Terse - pass/fail only, minimal output
  -t1  Human-readable - formatted for developers
  -t2  JSON - machine-readable test results
  -t3  JUnit XML - standard CI/CD format
  -t4  JSON with coverage data
  -t5  Verbose coverage breakdown
  -t6  HTML dashboard with visual coverage

CI/CD INTEGRATION
Use -t3 for Jenkins, GitHub Actions, GitLab CI:
  ek9 -t3 myTests.ek9
Produces standard JUnit XML that CI tools understand.

JSON FOR CUSTOM TOOLS
Use -t2 or -t4 for custom reporting pipelines:
  ek9 -t2 myTests.ek9

HTML DASHBOARD
Use -t6 for development review:
  ek9 -t6 myTests.ek9
Generates an interactive coverage dashboard.

See Q206 for coverage modes. See Q155 for basic testing. See Q157 for running tests. See Q321 for the HTML quality dashboard. See Q322 for profiling data. See Q627 for profiling output formats.

defines module qa.testdeep.formats

  defines program

    TestOutputFormatsDemo()
      stdout <- Stdout()

      // === 7 OUTPUT FORMATS ===

      stdout.println("EK9 test output formats:")
      stdout.println("  -t0  Terse (pass/fail only)")
      stdout.println("  -t1  Human-readable")
      stdout.println("  -t2  JSON results")
      stdout.println("  -t3  JUnit XML (CI/CD)")
      stdout.println("  -t4  JSON with coverage")
      stdout.println("  -t5  Verbose coverage")
      stdout.println("  -t6  HTML dashboard")

      // === CI/CD USAGE ===

      stdout.println("CI/CD: ek9 -t3 myTests.ek9")
      stdout.println("Development: ek9 -t6 myTests.ek9")

Java: JUnit XML via Maven Surefire, JaCoCo for coverage HTML. Python: pytest --junitxml and coverage html. Rust: cargo test with custom formatters. Go: go test -json, gotestsum for formatting. Kotlin: JUnit XML via Gradle. EK9: 7 built-in formats from -t0 to -t6, no external tools needed.

ci coverage format html json junit output pipeline terse test verbose xml

stdout.println("EK9 test output formats:")
      stdout <- Stdout()


Question: How do I test output containing dynamic values like dates, GUIDs, or timestamps?

How do I handle non-deterministic output in EK9 tests?

What placeholders can I use in expected output files?

How do I match dynamic values in EK9 test output?

Answer:
EK9 expected output files support type-aware placeholders that match dynamic values. Use {{Type}} syntax for values that change between test runs.

PLACEHOLDER SYNTAX
In expected_output.txt, use {{Type}} placeholders:
  Created at: {{DateTime}}
  ID: {{GUID}}
  Count: {{Integer}}

AVAILABLE PLACEHOLDERS
  {{String}}    matches any non-empty text
  {{Integer}}   matches whole numbers
  {{Float}}     matches decimal numbers
  {{Boolean}}   matches true/false
  {{Date}}      matches ISO dates
  {{DateTime}}  matches ISO datetimes
  {{GUID}}      matches UUID format
  {{Money}}     matches currency format

EXAMPLE EXPECTED OUTPUT FILE
For a program that prints a GUID and timestamp:
  Record ID: {{GUID}}
  Created: {{DateTime}}
  Items: {{Integer}}
  Active: {{Boolean}}

See Q204 for black-box testing. See Q205 for parameterized tests. See Q157 for running tests.

defines module qa.testdeep.placeholders

  defines program

    DynamicOutputDemo()
      stdout <- Stdout()

      // === DYNAMIC VALUES IN OUTPUT ===
      // When testing, some values change each run.
      // EK9 expected_output.txt supports placeholders.

      stdout.println("Dynamic output placeholders:")
      stdout.println("  {{String}}   - any non-empty text")
      stdout.println("  {{Integer}}  - whole numbers")
      stdout.println("  {{Float}}    - decimal numbers")
      stdout.println("  {{Boolean}}  - true or false")
      stdout.println("  {{Date}}     - ISO date format")
      stdout.println("  {{DateTime}} - ISO datetime format")
      stdout.println("  {{GUID}}     - UUID format")
      stdout.println("  {{Money}}    - currency format")

      // Example: a test printing dynamic values
      // expected_output.txt would contain:
      //   Record ID: {{GUID}}
      //   Created: {{DateTime}}
      //   Count: {{Integer}}

      stdout.println("Placeholders match dynamic values in output")

Java: Hamcrest matchers or regex in assertions. Python: unittest with regex or mock. Rust: custom test helpers with regex. Go: regex assertions in test functions. Kotlin: custom matchers. EK9: type-aware placeholders in expected output files, validated at test runtime.

anonymous capture closure coverage date dynamic expected format guid match output placeholder test timestamp

stdout.println("Dynamic output placeholders:")
      stdout <- Stdout()

Explanation: Variables must be declared before use. Using stdout before declaring it is a forward reference. See ek9 -h E50010 for details.


Question: How do I test that code throws or does not throw exceptions in EK9?

How do I verify exceptions in EK9 tests?

How do assertThrows and assertDoesNotThrow work in EK9?

Answer:
EK9 provides try/catch for testing exception behavior, plus assertThrows and assertDoesNotThrow keywords for direct exception testing.

TRY/CATCH FOR EXCEPTION TESTING
Use try/catch to verify an exception is thrown:
  try
    riskyFunction()
    assert false  // Should not reach here
  catch
    -> ex as Exception
    assert ex?
    stdout.println("Caught: " + $ex)

ASSERTTHROWS
Verify that a block throws an exception:
  assertThrows
    riskyFunction()
The test passes only if an exception is thrown.

ASSERTDOESNOTTHROW
Verify that a block completes without throwing:
  assertDoesNotThrow
    safeFunction()
The test fails if any exception is thrown.

TESTING EXCEPTION MESSAGES
Catch and inspect exception details:
  try
    throwError()
  catch
    -> ex as Exception
    assert $ex == "expected message"

See Q134 for try/catch basics. See Q136 for throwing exceptions. See Q156 for assertions.

See Q305 for require vs assert vs throw. See Q306 for assertThrows in depth. See Q307 for assertDoesNotThrow in depth.

defines module qa.testdeep.exceptions

  defines function

    riskyDivide() as pure
      ->
        a as Integer
        b as Integer
      <-
        rtn as Integer?

      require b?
      rtn: a / b

  defines program

    ExceptionTestingDemo()
      stdout <- Stdout()

      // === TRY/CATCH FOR EXCEPTION TESTING ===

      try
        result <- riskyDivide(10, 2)
        stdout.println(`10 / 2 = ${result}`)
      catch
        -> ex as Exception
        stdout.println("Unexpected error: " + $ex)

      // === TESTING ERROR CONDITIONS ===

      try
        discarded <- riskyDivide(10, 0)
        stdout.println("Should not reach here " + $discarded)
      catch
        -> ex as Exception
        stdout.println("Caught expected error: " + $ex)

      // === ASSERTTHROWS / ASSERTDOESNOTTHROW ===
      // In @Test programs:
      //   assertThrows
      //     riskyDivide(10, 0)
      //   assertDoesNotThrow
      //     riskyDivide(10, 2)

      stdout.println("assertThrows verifies exception is thrown")
      stdout.println("assertDoesNotThrow verifies no exception")

Java: JUnit assertThrows(() -> code) and assertDoesNotThrow(). Python: pytest.raises(ExceptionType). Rust: #[should_panic] attribute. Go: custom helper checking error returns. Kotlin: assertThrows<Exception> { code }. EK9: assertThrows/assertDoesNotThrow keywords, plus try/catch for detailed inspection.

assertDoesNotThrow assertThrows catch coverage error exception test throw verify

riskyDivide() as pure
      ->
        a as Integer
        b as Integer
      <-
        rtn as Integer?

      rtn: 0

Explanation: Both parameters a and b are declared but neither would be used in the function body if the implementation ignores them. All parameters must be referenced. See ek9 -h E08091 for details.


Question: How does trait delegation with 'by' work in EK9?

How do I delegate trait methods to a field in EK9?

What is the 'by' keyword for traits in EK9?

How do I use delegation instead of inheritance in EK9?

Answer:
EK9 supports trait delegation with the 'by' keyword. A class can implement a trait and delegate unoverridden methods to a field.

TRAIT DELEGATION SYNTAX
Declare a class that delegates trait methods:
  FilteredLogger with trait of Logger by delegate
Here 'delegate' is a field of type Logger. All Logger methods are forwarded to it unless overridden.

SELECTIVE OVERRIDE
Override only the methods you want to customize:
  override log()
    -> msg as String
    delegate.log("[FILTERED] " + msg)
The logLevel() method is automatically delegated.

WHY DELEGATION
Delegation avoids tight coupling from inheritance. You can swap the delegate at runtime and only customize the methods that need different behavior.

COMPOSITION BENEFIT
The class wraps and decorates the delegate without extending it. This follows the principle of favoring composition over inheritance.

See Q106 for trait basics. See Q109 for composition. See Q115 for dynamic classes. See Q212 for composition over inheritance. See Q264 for adapter pattern using delegation. See Q266 for cross-cutting concerns via delegation.

See Q334 for transaction decorator pattern.

defines module qa.patterns.delegation

  defines trait

    Logger
      log() as abstract
        -> msg as String

      logLevel() as abstract
        <- rtn as String?

  defines class

    ConsoleLogger with trait of Logger
      override log()
        -> msg as String
        Stdout().println("[LOG] " + msg)

      override logLevel()
        <- rtn as String: "INFO"

      default operator ?

    // Delegation: FilteredLogger delegates to ConsoleLogger
    // Only overrides log(), logLevel() is auto-delegated
    FilteredLogger with trait of Logger by delegate
      delegate as Logger: ConsoleLogger()

      FilteredLogger()
        -> logger as Logger
        this.delegate: logger

      override log()
        -> msg as String
        delegate.log("[FILTERED] " + msg)

      default operator ?

  defines program

    TraitDelegationDemo()
      stdout <- Stdout()

      // === BASIC LOGGER ===

      logger <- ConsoleLogger()
      logger.log("Hello from console")
      stdout.println(`Level: ${logger.logLevel()}`)

      // === DELEGATING LOGGER ===

      filtered <- FilteredLogger(logger)
      filtered.log("Hello through filter")

      // logLevel() is delegated to ConsoleLogger
      stdout.println(`Filtered level: ${filtered.logLevel()}`)

Java: no built-in delegation, manual forwarding methods. Kotlin: 'by' delegation on interfaces. Rust: no delegation, manual impl forwarding. Go: embedded structs provide forwarding. Python: __getattr__ for delegation. EK9: 'with trait of X by field' delegates all unoverridden methods to the field.

abstract by compose delegate delegation design idiom open override pattern proxy trait virtual

delegation pattern
by keyword delegation

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

Explanation: ConsoleLogger is closed by default and cannot be extended. EK9 types are closed unless declared 'as open'. Use trait delegation with 'by' instead of inheritance. See ek9 -h E05030 for details.


Question: How do I implement double dispatch in EK9?

How do I use dispatcher for the visitor pattern in EK9?

How do I replace the visitor pattern in EK9?

How does multi-method dispatch work in EK9?

Answer:
EK9 has built-in double dispatch via the 'as dispatcher' modifier. Mark a method as a dispatcher and provide overloads for specific types. The runtime selects the most specific match.

DISPATCHER METHOD
Mark the base method with 'as dispatcher':
  render() as dispatcher
    -> shape as Shape
This method is the dispatch entry point.

SPECIFIC OVERLOADS
Provide overloaded methods for specific subtypes:
  render()
    -> circle as Circle
    stdout.println("Rendering circle")
  render()
    -> rect as Rectangle
    stdout.println("Rendering rectangle")

RUNTIME DISPATCH
Calling render(someShape) dispatches to the most specific overload based on the actual runtime type of someShape.

NO VISITOR BOILERPLATE
Unlike the Visitor pattern, no accept() methods needed on element classes. The dispatcher handles type resolution automatically.

See Q60 for function dispatching. See Q103 for abstract classes. See Q105 for method dispatch. See Q254 for the Any type as dispatcher fallback. See Q255 for method resolution costs in dispatching.

defines module qa.patterns.doubledispatch

  defines class

    Shape as abstract
      shapeName as String?

      default private Shape() as pure

      Shape() as pure
        -> n as String
        shapeName :=? String(n)

      name() as pure
        <- rtn as String: String(shapeName)

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

    Circle is Shape

      Circle() as pure
        super("Circle")

    Rectangle is Shape

      Rectangle() as pure
        super("Rectangle")

    // Renderer with dispatcher-based double dispatch
    Renderer
      stdout as Stdout: Stdout()

      // Base dispatcher method
      render() as dispatcher
        -> shape as Shape
        stdout.println("Rendering generic shape: " + shape.name())

      // Specific overload for Circle
      render()
        -> circle as Circle
        stdout.println("Rendering circle: " + circle.name())

      // Specific overload for Rectangle
      render()
        -> rect as Rectangle
        stdout.println("Rendering rectangle: " + rect.name())

      default operator ?

  defines program

    DoubleDispatchDemo()

      renderer <- Renderer()

      // === DIRECT CALLS ===

      renderer.render(Circle())
      renderer.render(Rectangle())

      // === DISPATCH VIA BASE TYPE ===
      // When called with Shape reference,
      // dispatcher resolves to most specific overload

      shapes <- List() of Shape
      shapes += Circle()
      shapes += Rectangle()

      for shape in shapes
        renderer.render(shape)

Java: Visitor pattern with accept/visit methods, or instanceof chains. Kotlin: when + is checks with sealed classes. C++: double dispatch via RTTI or visitor. Rust: enum match (no double dispatch). Go: type switch. EK9: 'as dispatcher' modifier with overloaded methods, runtime selects most specific match.

design dispatch dispatcher double handler idiom multiple overload pattern runtime sealed type visitor

stdout.println("Rendering generic shape: " + shape.label())

Explanation: Shape does not have a label() method. The correct method name is name(). Calling a non-existent method triggers E50060 — method not resolved. See ek9 -h E50060 for details.

Oracle can generate a dispatcher with handler methods for a type hierarchy.


Question: How do I apply the composition-over-inheritance pattern in EK9?

Why does EK9 prefer composition over inheritance?

Answer:
EK9 types are closed by default, encouraging composition over inheritance. Wrap a collection or type as a private field and expose only the operations you need.

COMPOSITION PATTERN
Wrap a collection as a private field:
  TaskQueue
    items as List of String
Expose only the methods that make sense:
  addTask(), pendingCount(), hasWork()

MULTI-CONCERN COMPOSITION
Composition shines when wrapping multiple concerns. ValidatedConfig wraps a Dict and adds validation:
  ValidatedConfig
    entries as Dict of (String, String)
    get() with default, set() with validation, has() for existence
The Dict is hidden behind a controlled API that enforces business rules.

WHY COMPOSITION
Built-in types like List and Dict are closed (cannot be extended). This prevents mixing collection behavior with application logic. Composition gives you control over the public API.

WHEN INHERITANCE IS APPROPRIATE
Inheritance is still correct for genuine IS-A relationships. Use 'as open' on the base class and extend when the subtype truly specializes the base.

CONTROLLED INTERFACE
With composition, clients see only:
  config.get(key), config.set(key, value), config.has(key)
Not the full Dict API (merge, replace, iterate, etc.).

See Q101 for closed by default. See Q128 for closed collections. See Q109 for composition. See Q210 for trait delegation. See Q264 for adapter pattern using composition. See Q266 for cross-cutting concerns via delegation. See Q278 for why AI extends closed types. See Q315 for inheritance depth limits that encourage composition.

defines module qa.patterns.composition

  defines class

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

      default TaskQueue()

      addTask()
        -> task as String
        items += task

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

      hasWork() as pure
        <- rtn as Boolean: length items > 0

      default operator ?

    // Multi-concern composition: Dict + validation
    ValidatedConfig
      entries as Dict of (String, String): Dict() of (String, String)

      default ValidatedConfig()

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

      set()
        ->
          key as String
          setting as String
        <- rtn as Boolean: false
        if key? and setting?
          entries += DictEntry(key, setting)
          rtn: true

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

      entryCount() as pure
        <- rtn as Integer: length entries

      default operator ?

  defines program

    CompositionDemo()
      stdout <- Stdout()

      // === TASK QUEUE: LIST COMPOSITION ===

      queue <- TaskQueue()
      queue.addTask("Build feature")
      queue.addTask("Write tests")
      queue.addTask("Review code")

      stdout.println(`Pending tasks: ${queue.pendingCount()}`)
      stdout.println(`Has work: ${queue.hasWork()}`)

      // === VALIDATED CONFIG: DICT COMPOSITION ===

      config <- ValidatedConfig()
      hostName <- "host"
      config.set(hostName, "localhost")
      config.set("port", "8080")

      stdout.println(`Has host: ${config.has(hostName)}`)
      stdout.println(`Host: ${config.get(hostName)}`)
      stdout.println(`Entries: ${config.entryCount()}`)

      // Clients use get/set/has, not the full Dict API
      stdout.println("Composition controls the public API")

Java: ArrayList<T> is open, commonly extended (bad practice). Python: list is open. Rust: no inheritance, composition by default. Go: embedding for delegation. Kotlin: classes final by default like EK9. EK9: types closed by default, composition is the natural and preferred pattern.

closed composition delegate design encapsulate idiom inheritance migrate pattern wrap

composition over inheritance
composition pattern

Explanation: Dict does not have a get() method. Use getOrDefault() with a fallback value. Calling a non-existent method triggers E50060 — method not resolved. See ek9 -h E50060 for details.


Question: How do I protect shared data with MutexLock in EK9?

How do I use MutexLock with a callback in EK9?

How do I safely update locked data in EK9?

Answer:
MutexLock of T wraps a value and controls access through MutexKey callback functions. The canonical EK9 pattern is to hold the lock as a FIELD on a wrapping class and expose the protected operations as class methods. The lock never escapes the wrapping class.

FIELD-ON-CLASS PATTERN
Declare the MutexLock as a field with an inline initialiser:
  Inventory
    lockedStock as MutexLock of List of String: MutexLock(List() of String)

The field initialiser is the only legal MutexLock construction site (E08254/E08256 reject locals and collections of locks).

MUTEXKEY CALLBACK
Inside each method on the wrapping class, declare a dynamic MutexKey function. The function body is the critical section; lockedItem is the protected value:
  add()
    -> item as String
    accessKey <- (item) is MutexKey of List of String as function
      lockedItem += item
    require lockedStock.enter(accessKey)

ENTER THE LOCK
Call enter() (blocking) or tryEnter() (non-blocking) inside the class's own method. The lock releases automatically when the callback returns.

COPY DATA OUT
To expose a snapshot of the protected value, copy it out with :=: into a return-target captured in the callback:
  read()
    <- rtn as List of String: List() of String
    accessKey <- (rtn) is MutexKey of List of String as function
      rtn :=: lockedItem
    require lockedStock.enter(accessKey)

WHY FIELD-ON-CLASS
EK9's compile-time data-race detection needs every lock to have a stable field-rooted home. Locks declared as locals or held inside collections defeat the static analysis. Wrapping the lock in a class also keeps the API surface small — callers see only the protected operations.

See Q158 for MutexLock basics. See Q159 for parallel processing. See Q52 for dynamic functions. See Q267 for anti-patterns including mutable shared state.

defines module qa.patterns.mutex

  defines class

    //Canonical pattern: MutexLock as a field on a wrapping class.
    //The class methods are the only API to the protected stock list;
    //the lock itself never escapes.
    Inventory
      lockedStock as MutexLock of List of String: MutexLock(List() of String)

      add()
        -> item as String

        accessKey <- (item) is MutexKey of List of String as function
          lockedItem += item

        require lockedStock.enter(accessKey)

      read()
        <- rtn as List of String: List() of String

        accessKey <- (rtn) is MutexKey of List of String as function
          rtn :=: lockedItem

        require lockedStock.enter(accessKey)

      default operator ?

  defines program

    MutexPatternDemo()
      stdout <- Stdout()

      //Construct the wrapper — the MutexLock is created by the
      //class's field initialiser, not here in user code.
      inventory <- Inventory()

      inventory.add("widgets")
      inventory.add("sprockets")

      snapshot <- inventory.read()
      stdout.println(`Inventory snapshot: ${$snapshot}`)

Java: synchronized blocks or ReentrantLock with try/finally. Python: threading.Lock with context manager. Rust: Mutex<T>::lock() returns MutexGuard. Go: sync.Mutex with Lock()/Unlock(). EK9: MutexLock as a class field, accessed through MutexKey callbacks inside class methods. The field-rooted home enables compile-time data-race and deadlock detection.

class concurrent design enter field idiom key lock mutex pattern protect safety shared thread

lock <- MutexLock(List() of String)   //local — rejected

Explanation: MutexLock must be declared as a field on a class with an inline initialiser. The field-rooted home is required for compile-time lock-identity tracking. See ek9 -h E08256 for details.


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

How do I swap algorithms at runtime in EK9?

How do I use functions as strategies in EK9?

How do I inject behavior via function parameters in EK9?

Answer:
EK9 implements the strategy pattern using abstract functions and dynamic function implementations. Pass different function instances to swap behavior.

ABSTRACT FUNCTION AS STRATEGY
Define the strategy interface as an abstract function:
  Formatter as abstract function
    -> item as String
    <- rtn as String?

DYNAMIC IMPLEMENTATIONS
Create concrete strategies with dynamic functions:
  upperFormatter <- () is Formatter as function
    rtn: item.upperCase()
  lowerFormatter <- () is Formatter as function
    rtn: item.lowerCase()

PROCESSOR WITH STRATEGY
Pass the strategy as a parameter:
  Processor
    process()
      ->
        formatter as Formatter
        text as String
      <- rtn as String: formatter(text)

SWAPPING STRATEGIES
Use different formatters without changing Processor:
  processor.process(upperFormatter, "hello")
  processor.process(lowerFormatter, "HELLO")

See Q57 for strategy without subclassing. See Q52 for dynamic functions. See Q55 for function delegation. See Q210 for trait delegation as an alternative to function strategies.

defines module qa.patterns.strategy

  defines function

    Formatter() as abstract
      -> item as String
      <- rtn as String?

  defines class

    TextProcessor
      process()
        ->
          formatter as Formatter
          text as String
        <- rtn as String: formatter(text)

      default operator ?

  defines program

    StrategyPatternDemo()
      stdout <- Stdout()

      // === DYNAMIC FUNCTION STRATEGIES ===

      upperFormatter <- () is Formatter as function
        rtn: item.upperCase()

      lowerFormatter <- () is Formatter as function
        rtn: item.lowerCase()

      // === PROCESSOR USES STRATEGY ===

      textProcessor <- TextProcessor()

      upper <- textProcessor.process(upperFormatter, "hello world")
      stdout.println(`Upper: ${upper}`)

      lower <- textProcessor.process(lowerFormatter, "HELLO WORLD")
      stdout.println(`Lower: ${lower}`)

      // === SWAP STRATEGIES FREELY ===

      stdout.println("Strategy pattern: swap behavior via function parameters")

Java: Strategy interface with implementing classes. Python: pass functions directly (first-class). Rust: trait objects or closures. Go: function types. Kotlin: function types or interface implementations. EK9: abstract function as strategy interface, dynamic functions as implementations.

algorithm behavior delegate design flexible function idiom inject pattern strategy swap

Explanation: Abstract functions define a strategy interface with no implementation. Using 'as open' instead of 'as abstract' triggers E07110 because 'open' is for functions with a body that can be overridden, not for bodyless declarations. See ek9 -h E07110 for details.


Question: How does the 'sanitized' parameter modifier work in EK9?

How do I mark parameters as sanitized in EK9?

How do I handle user input safely in EK9?

Answer:
The 'sanitized' modifier on parameters tells the compiler to track input that needs careful handling. It enables compile-time input validation tracking.

SANITIZED PARAMETER
Mark a parameter as sanitized:
  processInput() as pure
    -> input as sanitized String
The compiler knows this parameter contains external, potentially dangerous data.

COPY CONSTRUCTOR PATTERN
In pure contexts, create a safe local copy:
  localCopy <- String(input)
This breaks any reference aliasing and creates independent data.

NON-PURE CONTEXT
In non-pure functions, use :=: or :~: on initialized variables:
  safeCopy <- String(sql)
  result: ""
  result :=: safeCopy

DIRECT USE IN EXPRESSIONS
Using sanitized parameters in expressions is safe because it creates new values:
  output: "Processed: " + input

See Q216 for threat detection. See Q217 for pure function interaction. See Q49 for function basics. See Q54 for pure functions. See Q269 for input validation. See Q271 for secrets and environment variables. See Q272 for defense in depth.

See Q661 for sanitized parameter restrictions. See Q662 for safe copy patterns. See Q663 for override matching.

defines module qa.security.sanitized

  defines function

    // Pure function with sanitized parameter
    pureProcess() as pure
      -> input as sanitized String
      <- result as String?

      // Copy constructor creates safe local copy
      localCopy <- String(input)
      result: "Processed: " + localCopy

    // Non-pure function with sanitized parameter
    queryProcess()
      -> sql as sanitized String
      <- result as String?

      // Copy constructor first
      safeCopy <- String(sql)
      result: ""
      result :=: safeCopy

    // Direct expression use is safe
    logInput() as pure
      -> userInput as sanitized String
      <- output as String?

      // Concatenation creates new string
      output: "Log: " + userInput

  defines program

    SanitizedParameterDemo()
      stdout <- Stdout()

      // === PURE FUNCTION WITH SANITIZED ===

      query <- "SELECT * FROM users"
      result1 <- pureProcess(query)
      if result1?
        stdout.println(result1)

      // === NON-PURE WITH COPY ===

      result2 <- queryProcess(query)
      if result2?
        stdout.println("Query: " + result2)

      // === DIRECT EXPRESSION ===

      result3 <- logInput("user data")
      if result3?
        stdout.println(result3)

Java: no language-level sanitization, relies on OWASP libraries. Python: no built-in taint tracking. Rust: newtype pattern for manual tracking. Go: no taint tracking. Kotlin: no sanitization modifier. EK9: 'sanitized' keyword on parameters enables compile-time tracking of tainted data.

compile-time copy injection input protect safe sanitized security tainted validation

sanitized
input sanitization
XSS prevention

Explanation: String does not have a clone() method. Use the copy constructor String(value) to create a defensive copy. Calling a non-existent method triggers E50060 — method not resolved. See ek9 -h E50060 for details.


Question: How does EK9 detect and report security threats?

What security threats does EK9 detect at compile time?

What security analysis does the EK9 compiler perform?

Answer:
EK9 detects common security threats at compile time through sanitized parameter tracking and static analysis.

SQL INJECTION DETECTION
When a sanitized String parameter is used in database contexts, the compiler tracks that untrusted data flows through the system, ensuring copy constructor patterns are used.

XSS DETECTION
Sanitized parameters used in output contexts are tracked. The copy constructor pattern ensures tainted HTML/JavaScript cannot pass through unchecked.

COMMAND INJECTION DETECTION
Sanitized parameters in command execution contexts are flagged. The compiler ensures defensive copies prevent shell injection.

COMPILE-TIME ENFORCEMENT
The compiler enforces the sanitization pattern:
  1. Mark external input as sanitized
  2. Create defensive copies via constructors
  3. Use copies for processing
Violating this pattern produces compile errors.

See Q215 for sanitized parameters. See Q134 for exception handling. See Q218 for security best practices. See Q268 for OWASP vulnerability prevention. See Q272 for defense in depth.

defines module qa.security.threats

  defines function

    // Sanitized parameter with copy constructor pattern
    processUserQuery() as pure
      -> userInput as sanitized String
      <- result as String?

      // Defensive copy — prevents injection
      safeCopy <- String(userInput)
      result: "Safe query: " + safeCopy

    // Passing sanitized to another function
    handleRequest() as pure
      -> requestData as sanitized String
      <- result as String?

      // Pass to helper for processing
      result: processUserQuery(requestData)

  defines program

    ThreatDetectionDemo()
      stdout <- Stdout()

      // === COMPILE-TIME SECURITY ===

      rawInput <- "user-provided data"
      safe <- processUserQuery(rawInput)
      if safe?
        stdout.println(safe)

      // === THREAT CATEGORIES ===

      stdout.println("EK9 detects at compile time:")
      stdout.println("  SQL injection via sanitized tracking")
      stdout.println("  XSS via output context analysis")
      stdout.println("  Command injection via parameter flow")
      stdout.println("Copy constructor pattern prevents all three")

Java: OWASP ESAPI or FindBugs for taint analysis (external tools). Python: Bandit for security linting. Rust: no taint tracking, but ownership prevents some classes. Go: no built-in, gosec for analysis. EK9: compile-time sanitization tracking detects SQL, XSS, and command injection threats.

command detection injection migrate protect report safe security sql threat xss


Question: How do sanitized parameters interact with pure functions in EK9?

Why must pure functions use copy constructors with sanitized parameters?

How do purity and sanitization work together in EK9?

What is the copy constructor pattern for sanitized parameters?

Answer:
In pure functions, sanitized parameters must use the copy constructor pattern because reassignment is forbidden. Purity and security reinforce each other.

PURE + SANITIZED = COPY CONSTRUCTOR
Pure functions cannot reassign, so the only option is:
  pureProcess() as pure
    -> input as sanitized String
    <- result as String?
    localCopy <- String(input)
    result: localCopy
The copy constructor creates an independent value.

WHY THEY INTERACT
Purity prevents mutation, sanitization tracks tainted data. Together they force the safest pattern: create a defensive copy and use only the copy. No escape hatches.

NON-PURE ALTERNATIVES
Non-pure functions have additional options:
  safeCopy <- String(input)
  result: ""
  result :=: safeCopy     // copy operator
  result :~: safeCopy     // merge operator

EXPRESSION USE
Both pure and non-pure can use sanitized parameters directly in expressions:
  result: "Prefix: " + input
Concatenation creates a new value, so no aliasing risk.

See Q215 for sanitized basics. See Q54 for pure functions. See Q141 for constant immutability. See Q273 for purity as security boundary.

See Q662 for safe copy patterns. See Q666 for injection pure context.

defines module qa.security.pure

  defines function

    // Pure function MUST use copy constructor
    pureWithSanitized() as pure
      -> input as sanitized String
      <- result as String?

      // Only option in pure context: copy constructor
      cleanInput <- String(input)
      result: "Clean: " + cleanInput

    // Non-pure has more options
    nonPureWithSanitized()
      -> input as sanitized String
      <- result as String?

      // Option 1: Copy constructor (works everywhere)
      safeCopy <- String(input)

      // Option 2: :=: on initialized variable
      result: ""
      result :=: safeCopy

    // Direct expression use works in both contexts
    directUse() as pure
      -> input as sanitized String
      <- result as String?

      // Concatenation creates new value — safe
      result: "Output: " + input

  defines program

    SanitizedPureDemo()
      stdout <- Stdout()

      userInput <- "external input"

      // === PURE + SANITIZED ===

      result1 <- pureWithSanitized(userInput)
      if result1?
        stdout.println(result1)

      // === NON-PURE + SANITIZED ===

      result2 <- nonPureWithSanitized(userInput)
      if result2?
        stdout.println(result2)

      // === DIRECT EXPRESSION ===

      result3 <- directUse(userInput)
      if result3?
        stdout.println(result3)

      stdout.println("Purity + sanitization = forced defensive copies")

Java: no purity concept, PreparedStatement for SQL safety. Python: no purity enforcement. Rust: ownership model prevents some aliasing. Go: no purity. Kotlin: no purity. Haskell: purity prevents side effects but no sanitization concept. EK9: purity and sanitization synergize, forcing defensive copy pattern.

constructor context copy defensive function immutable protect pure purity safe sanitize sanitized security side-effect

Explanation: String does not have a copy() method. Use the copy constructor String(value) to create a defensive copy. Calling a non-existent method triggers E50060 — method not resolved. See ek9 -h E50060 for details.


Question: What are the security best practices in EK9?

What security features does EK9 provide?

How do I prevent security vulnerabilities in EK9?

Answer:
EK9 provides multiple security features that work together. Following these best practices prevents common vulnerability classes.

SANITIZED PARAMETERS
Mark external input with 'sanitized':
  -> userInput as sanitized String
The compiler tracks tainted data flow.

COPY CONSTRUCTOR PATTERN
Always create defensive copies:
  localCopy <- String(userInput)
Prevents reference aliasing and injection.

PURE FUNCTIONS
Use pure functions for data processing:
  process() as pure
Purity prevents side effects and forces safe data handling.

REQUIRE VALIDATION
Use require for input validation:
  require input?
  require length input > 0
Fails fast on invalid input.

MUTEXLOCK FOR SHARED STATE
Protect shared data with a MutexLock held as a FIELD on a wrapping class (locals are rejected by E08256):
  SharedCache
    locked as MutexLock of Dict of (String, String): MutexLock(Dict() of (String, String))
The wrapping class's methods are the only API to the protected data; MutexKey callbacks define the critical sections. The field-rooted home enables compile-time data-race detection. See Q158.

TYPE SAFETY
EK9's strong typing prevents type confusion attacks. No implicit conversions, no null references.

See Q215 for sanitized parameters. See Q29 for unset variables. See Q158 for MutexLock.

See Q215 for sanitized parameters. See Q217 for sanitized and pure. See Q268 for OWASP vulnerability prevention. See Q272 for defense in depth.

defines module qa.security.bestpractices

  defines function

    // Best practice: sanitized + pure + copy constructor
    secureProcess() as pure
      -> input as sanitized String
      <- result as String?

      // 1. Copy constructor for defensive copy
      cleanInput <- String(input)

      // 2. Validate with require
      require cleanInput?

      // 3. Process the clean copy
      result: "Secure: " + cleanInput

  defines program

    SecurityBestPracticesDemo()
      stdout <- Stdout()

      userInput <- "user input"

      // === SANITIZED + PURE + REQUIRE ===

      result <- secureProcess(userInput)
      if result?
        stdout.println(result)

      // === SECURITY FEATURES SUMMARY ===

      stdout.println("EK9 security best practices:")
      stdout.println("  1. sanitized on external input")
      stdout.println("  2. Copy constructor for defensive copies")
      stdout.println("  3. Pure functions for processing")
      stdout.println("  4. require for input validation")
      stdout.println("  5. MutexLock for shared state")
      stdout.println("  6. Strong typing prevents confusion")

Java: OWASP guidelines, Spring Security, manual defensive coding. Python: input validation libraries, no compile-time safety. Rust: ownership model prevents many bugs. Go: manual security practices. Kotlin: null safety helps but no sanitization. EK9: sanitized keyword, pure functions, require validation, MutexLock, and strong typing provide layered security.

best immutable mutex practices protect pure require safe safety sanitized security side-effect type

Explanation: String does not have a sanitize() method. Use the copy constructor String(value) to create a defensive copy. The sanitized modifier on the parameter is the security mechanism, not a method call. See ek9 -h E50060 for details.


Question: What do I get for free with an EK9 enumeration?

What operators does an EK9 enum automatically have?

How many members does an EK9 enumeration generate?

What comes built-in with EK9 enumerations?

Answer:
From a bare list of values you get 28 auto-generated members: 24 operators, 3 constructors, and an iterator. No annotations, no derive macros, no boilerplate.

COMPARISON OPERATORS (7 enum-vs-enum)
Every enumeration gets ==, <>, <, >, <=, >=, and <=> for full ordering based on declaration order. No Comparable interface to implement.

STRING COMPARISON OPERATORS (7 enum-vs-String)
The same seven comparison operators work with String arguments directly: season == "Spring" works without any conversion.

CONVERSION OPERATORS
$ converts to String, #^ promotes to String, $$ converts to JSON, and #? returns a hashcode Integer.

QUERY OPERATORS
? checks if the enum is set. #< returns the first declared member. #> returns the last declared member.

MUTATING OPERATORS
++ advances to the next member. -- goes to the previous member. Both go to unset at boundaries.

CONSTRUCTORS (3)
Colour() creates unset. Colour("Red") creates from string (unset if invalid). Colour(existing) copies.

ITERATOR
'for colour in Colour' iterates all members. 'cat Colour' streams them.

See Q99 for basic enumeration syntax. See Q220 for navigation with increment and decrement. See Q221 for string comparison details. See Q96 for class operators.

defines module qa.enums.batteries

  defines type

    Colour
      Red
      Green
      Blue

  defines program

    EnumBatteriesDemo()
      stdout <- Stdout()

      red <- Colour.Red
      blue <- Colour.Blue

      // === COMPARISON OPERATORS (enum vs enum) ===

      stdout.println(`Equal: ${red == Colour.Red}`)
      stdout.println(`Not equal: ${red <> blue}`)
      stdout.println(`Less than: ${red < blue}`)
      stdout.println(`Greater than: ${blue > red}`)
      stdout.println(`Less or equal: ${red <= Colour.Red}`)
      stdout.println(`Greater or equal: ${blue >= red}`)
      stdout.println(`Compare: ${red <=> blue}`)

      // === STRING COMPARISON OPERATORS ===

      stdout.println(`Eq string: ${red == "Red"}`)
      stdout.println(`Neq string: ${red <> "Blue"}`)
      stdout.println(`Lt string: ${red < "Green"}`)

      // === CONVERSION OPERATORS ===

      stdout.println(`String: ${red}`)
      stdout.println(`Promote: ${#^red}`)
      stdout.println(`JSON: ${$red}`)
      stdout.println(`Hashcode: ${#?red}`)

      // === QUERY OPERATORS ===

      stdout.println(`IsSet: ${red?}`)
      stdout.println(`First: ${#< red}`)
      stdout.println(`Last: ${#> red}`)

      // === CONSTRUCTORS ===

      fromString <- Colour("Green")
      stdout.println(`From string: ${fromString}`)

      invalid <- Colour("Purple")
      stdout.println(`Invalid isSet: ${invalid?}`)

      unsetColour <- Colour()
      stdout.println(`Unset isSet: ${unsetColour?}`)

      copied <- Colour(red)
      stdout.println(`Copied: ${copied}`)

      // === ITERATION ===

      for colour in Colour
        stdout.println(`Iterate: ${colour}`)

      colours <- cat Colour | collect as List of Colour
      stdout.println(`Collected: ${length colours}`)

Java: toString(), valueOf(), compareTo(), equals() generated but no first/last, no increment/decrement, no string-parameter comparison, no JSON, no safe string construction. Python: needs @total_ordering for ordering, no iteration operators, no JSON. Rust: needs #[derive(PartialOrd, Ord, PartialEq, Eq)] plus strum crate for iteration and string conversion. Go: hand-written everything, iota gives integers only. Kotlin: entries property but no ordering operators, no increment/decrement, no JSON. Swift: CaseIterable for iteration, Comparable conformance needed for ordering, no increment/decrement, no string construction from String, associated values add power but also complexity. C#: limited to integer-based comparison, no string construction. EK9: 28 members from a bare value list, zero boilerplate.

automatic batteries boilerplate built-in enumeration free generated included migrate operator swift

Explanation: EK9 enumerations have no toString() method — use the $ operator (or plain ${...} interpolation) for String conversion. See ek9 -h E50060 for details.


Question: How do I navigate between enum values in EK9?

How do increment and decrement work on EK9 enums?

What happens when you go past the last enum value?

How do I get the first or last enum value in EK9?

Answer:
EK9 enumerations support ++ (increment), -- (decrement), #< (first), and #> (last) operators for navigating between values.

INCREMENT AND DECREMENT
The ++ operator moves to the next declared value. The -- operator moves to the previous. Both are mutating operators that change the variable in place.

BOUNDARY TO UNSET
Incrementing past the last value makes the enum unset. Decrementing before the first value also makes it unset. No ArrayIndexOutOfBounds. No wrap-around confusion. The result is safe, predictable, and testable with the ? operator.

FIRST AND LAST
The #< operator always returns the first declared value. The #> operator always returns the last. These work on any set enum variable and return a set result regardless of which value the variable holds.

SAFE CYCLING
To safely cycle through enum values, check with ? after each increment. When the result becomes unset, you have exhausted all values.

See Q219 for all auto-generated operators. See Q99 for basic enumeration. See Q222 for unset semantics.

defines module qa.enums.navigation

  defines type

    TrafficLight
      Red
      Amber
      Green

  defines program

    EnumNavigationDemo()
      stdout <- Stdout()

      // === INCREMENT THROUGH VALUES ===

      light <- TrafficLight.Red
      stdout.println(`Start: ${light}`)

      light++
      stdout.println(`After ++: ${light}`)

      light++
      stdout.println(`After ++ again: ${light}`)

      // === BOUNDARY: increment past last goes unset ===

      light++
      stdout.println(`Past last isSet: ${light?}`)

      // === DECREMENT THROUGH VALUES ===

      current <- TrafficLight.Green
      stdout.println(`Start: ${current}`)

      current--
      stdout.println(`After --: ${current}`)

      current--
      stdout.println(`After -- again: ${current}`)

      // === BOUNDARY: decrement before first goes unset ===

      current--
      stdout.println(`Before first isSet: ${current?}`)

      // === FIRST AND LAST ===

      mid <- TrafficLight.Amber
      stdout.println(`First: ${#< mid}`)
      stdout.println(`Last: ${#> mid}`)

      // === SAFE CYCLING ===

      item <- TrafficLight.Red
      while item?
        stdout.println(`Cycle: ${item}`)
        item++

Java: ordinal() + 1 with manual bounds checking, risk of ArrayIndexOutOfBounds, no built-in next/previous methods. Python: no built-in next or previous on Enum, must use list indexing with manual bounds. Rust: no built-in increment/decrement, must implement manually or use strum crate. Go: can increment iota integer but no bounds safety, wraps silently. C#: can cast to integer and increment but no bounds checking. Kotlin: ordinal + 1 with values() array, manual bounds required. EK9: ++ and -- with automatic boundary-to-unset semantics, completely safe.

boundary cycle decrement enum enumeration first increment last migrate navigate next previous unset

Explanation: EK9 enumerations do not have next() or previous() methods. Use the ++ operator to advance to the next value and -- to go to the previous value. These are mutating operators built into the enum type. See ek9 -h E50060 for details.


Question: How can I compare an enum directly to a string in EK9?

Can I use == to compare an enum to a string in EK9?

How does string comparison work with EK9 enumerations?

How do I construct an enum from a string safely in EK9?

Answer:
EK9 enumerations support 7 string-parameter comparison operators: ==, <>, <, >, <=, >=, and <=>. You can also construct an enum from a string safely.

DIRECT STRING COMPARISON
Compare any enum variable directly to a string literal: season == "Spring" returns true if the enum holds Spring. No .name(), no .toString(), no conversion needed.

ALL SEVEN OPERATORS
All comparison operators work with strings: ==, <>, <, >, <=, >=, <=>. Ordering follows declaration order, just like enum-vs-enum comparison.

SAFE STRING CONSTRUCTION
Season("Autumn") returns a set enum if the string matches a declared value. Season("Monsoon") returns an unset enum, not an exception.

GUARD EXPRESSION PATTERN
Use guard expressions for the cleanest pattern: 'if parsed <- Season("Summer")' only enters the block when the enum is set. Combines construction and checking in one line.

NO EXCEPTIONS
In Java, valueOf("bad") throws IllegalArgumentException. In Python, Enum["bad"] raises KeyError. In EK9, you simply get unset. No try-catch needed, just use a guard expression.

See Q219 for all auto-generated operators. See Q222 for unset propagation. See Q99 for basic enumeration.

defines module qa.enums.stringcompare

  defines type

    Season
      Spring
      Summer
      Autumn
      Winter

  defines program

    EnumStringCompareDemo()
      stdout <- Stdout()

      season <- Season.Spring

      // === DIRECT STRING COMPARISON ===

      springName <- "Spring"
      stdout.println(`Equals Spring: ${season == springName}`)
      stdout.println(`Not Winter: ${season <> "Winter"}`)

      // === ALL SEVEN STRING COMPARISON OPERATORS ===

      stdout.println(`Less than Summer: ${season < "Summer"}`)
      stdout.println(`Greater than Autumn: ${season > "Autumn"}`)
      stdout.println(`Leq Spring: ${season <= springName}`)
      stdout.println(`Geq Spring: ${season >= springName}`)
      stdout.println(`Compare to Winter: ${season <=> "Winter"}`)

      // === SAFE STRING CONSTRUCTION ===

      valid <- Season("Autumn")
      stdout.println(`Valid: ${valid}, isSet: ${valid?}`)

      invalid <- Season("Monsoon")
      stdout.println(`Invalid isSet: ${invalid?}`)

      // === GUARD EXPRESSION WITH STRING CONSTRUCTION ===

      if parsed <- Season("Summer")
        stdout.println(`Parsed: ${parsed}`)

      if missing <- Season("Rainy")
        stdout.println("This will not print")

Java: valueOf() throws IllegalArgumentException for invalid strings, name() and toString() for conversion, no direct string comparison operators. Python: Enum["name"] raises KeyError, must wrap in try/except. Rust: from_str requires implementing or deriving FromStr trait, returns Result. Go: manual string matching with switch or map lookup. C#: Enum.Parse() throws ArgumentException, Enum.TryParse() exists but returns bool. Kotlin: valueOf() throws IllegalArgumentException, entries.find{} for safe lookup. EK9: direct == comparison, safe string construction returns unset not exception.

compare comparison convert direct enum enumeration exception match migrate name safe string

Explanation: EK9 enumerations do not have a 'name()' method like Java enums. Use the == operator directly to compare an enum to a string. All seven comparison operators work with String arguments natively. See ek9 -h E50060 for details.