Question: I have several functions that all take the same 4 arguments. How do I clean this up in EK9?

How do I reduce repeated parameters across multiple functions?

Should I extract a record when many functions share the same arguments?

What is the idiomatic way to handle repeated function parameters in EK9?

Answer:
Extract a record. Group the repeated parameters into a record type and pass that instead.

BEFORE (repeated arguments):
  calculateTotal() as pure
    -> price as Float, quantity as Integer, discount as Float, taxRate as Float
    <- rtn as Float: ...

  formatInvoice() as pure
    -> price as Float, quantity as Integer, discount as Float, taxRate as Float
    <- rtn as String: ...

  isValidOrder() as pure
    -> price as Float, quantity as Integer, discount as Float, taxRate as Float
    <- rtn as Boolean: ...

AFTER (record parameter):
  defines record
    OrderLine
      price as Float: 0.0
      quantity as Integer: 0
      discount as Float: 0.0
      taxRate as Float: 0.0
      default operator

  calculateTotal() as pure
    -> order as OrderLine
    <- rtn as Float: ...

  formatInvoice() as pure
    -> order as OrderLine
    <- rtn as String: ...

Benefits:
- One place to add a new field (not 4 function signatures)
- Record fields are public so functions access them directly
- Records get default operators (==, $, #?, etc.) for free
- The record is a reusable type across your module

See Q97 for record basics. See Q1060 for record fields being public. See Q116 for default operator.

defines module qa.classesandoop.extractrecord

  defines record

    OrderLine
      price as Float: 0.0
      quantity as Integer: 0
      discount as Float: 0.0
      taxRate as Float: 0.0

      OrderLine()
        ->
          price as Float
          quantity as Integer
          discount as Float
          taxRate as Float
        this.price :=: price
        this.quantity :=: quantity
        this.discount :=: discount
        this.taxRate :=: taxRate

      default operator

  defines function

    calculateTotal() as pure
      -> order as OrderLine
      <- rtn as Float?

      subtotal <- order.price * order.quantity
      discounted <- subtotal - (subtotal * order.discount)
      rtn: discounted + (discounted * order.taxRate)

    formatInvoice() as pure
      -> order as OrderLine
      <- rtn as String: `${order.quantity} x ${order.price} = ${calculateTotal(order)}`

    isValidOrder() as pure
      -> order as OrderLine
      <- rtn as Boolean: order.price? and order.quantity? and order.quantity > 0

  defines program

    ExtractRecordDemo()
      stdout <- Stdout()

      order <- OrderLine(price: 29.99, quantity: 3, discount: 0.10, taxRate: 0.20)

      if isValidOrder(order)
        stdout.println(formatInvoice(order))
        stdout.println(`Total: ${calculateTotal(order)}`)

Java: extract a parameter object or record. Python: use a dataclass. EK9: extract a record with default operator.

arguments extract idiomatic parameters record refactor repeated

extract record from parameters
reduce repeated arguments

    calculateTotal() as pure
      -> p as Float, q as Integer, d as Float, t as Float
      <- rtn as Float: p * q

Explanation: Single-letter parameter names hide their purpose. Extract a record with descriptive field names and pass that instead.


Question: Write an EK9 class with comparison operators so I can sort a list of them.

Write a class I can use in a sorted stream pipeline

Code a class with all comparison operators

Answer:
Define the <=> (spaceship) operator, then derive <, >, <=, >= from it. Add #? for hashcode and $ for string display.

defines class
  Product
    productName as String: String()
    price as Float: 0.0

    Product()
      -> productName as String, price as Float
      this.productName :=: productName
      this.price :=: price

    operator <=> as pure
      -> other as Product
      <- rtn as Integer: price <=> other.price

    operator < as pure
      -> other as Product
      <- rtn as Boolean: (this <=> other) < 0

    operator > as pure
      -> other as Product
      <- rtn as Boolean: (this <=> other) > 0

    operator $ as pure
      <- rtn as String: `${productName}: ${price}`

Now you can sort:
  cat products | sort > stdout

See Q1049 for comparison on records. See Q1047 for hashcode. See Q1055 for default operator.

defines module qa.functions.writesortableclass

  defines class

    Product
      productName as String: String()
      price as Float: 0.0

      default private Product()

      Product()
        ->
          productName as String
          price as Float
        this.productName :=: productName
        this.price :=: price

      operator <=> as pure
        -> other as Product
        <- rtn as Integer: price <=> other.price

      operator < as pure
        -> other as Product
        <- rtn as Boolean: (this <=> other) < 0

      operator > as pure
        -> other as Product
        <- rtn as Boolean: (this <=> other) > 0

      operator <= as pure
        -> other as Product
        <- rtn as Boolean: (this <=> other) <= 0

      operator >= as pure
        -> other as Product
        <- rtn as Boolean: (this <=> other) >= 0

      operator #? as pure
        <- rtn as Integer: #?productName + #?price

      override operator ? as pure
        <- rtn as Boolean: productName? and price?

      operator $ as pure
        <- rtn as String: `${productName}: ${price}`

  defines program

    SortableClassDemo()
      stdout <- Stdout()

      products <- [
        Product("Widget", 9.99),
        Product("Gadget", 24.99),
        Product("Doohickey", 4.99)
      ]

      //Sort by price (uses <=> operator)
      stdout.println("Sorted by price:")
      cat products | sort > stdout

      //Get cheapest
      cheapest <- Product("Widget", 9.99) <? Product("Gadget", 24.99)
      stdout.println(`Cheapest: ${cheapest}`)

Java: implements Comparable<T>. Python: __lt__, __gt__. Rust: derive Ord. EK9: define <=> then derive comparison operators.

class code coder comparison operator sort write

write sortable class
comparison operators

Explanation: EK9 has no 'implements' keyword for interfaces. Define operators directly on the class.


Question: Does EK9 have list comprehensions like Python?

Can I write [x*2 for x in items] in EK9?

Is there a list comprehension syntax in EK9?

How do I create a filtered list in one line in EK9?

Answer:
No. EK9 has NO list comprehensions. This syntax does not exist:
  [x*2 for x in items if x > 0]
  [x for x in items when x > 0]

These are Python syntax. EK9 does not have them.

Use a stream pipeline instead:
  results <- cat items
    | filter by isPositive
    | map with doubleIt
    | collect as List of Integer

The stream pipeline is more powerful than list comprehensions:
- Named functions make the intent clear
- Pipeline stages can be reordered easily
- head, tail, sort, group, flatten are available
- The result type is explicit

Define the helper functions:
  isPositive() as pure
    -> item as Integer
    <- rtn as Boolean: item > 0

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

See Q1061 for reusable filter functions. See Q1062 for transform functions. See Q1063 for a complete program.

defines module qa.aimistakes.nolistcomprehensions

  defines function

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

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

  defines program

    NoComprehensionDemo()
      stdout <- Stdout()

      items <- [-3, 5, -1, 8, 0, 12, -7, 3]

      //Stream pipeline replaces list comprehension
      results <- cat items
        | filter by isPositive
        | map with doubleIt
        | collect as List of Integer
      stdout.println(`Doubled positives: ${results}`)

Python: [x*2 for x in items if x > 0]. EK9: cat items | filter by pred | map with fn | collect as Type. No list comprehensions exist in EK9.

comprehension list mistake pipeline python stream

no list comprehensions
use stream pipeline

results <- [x*2 for x in items if x > 0]

Explanation: EK9 has no list comprehension syntax. Use a stream pipeline: cat source | filter by fn | map with fn | collect as Type.


Question: What is the difference between < and <? in EK9?

Contrast the < comparison with the <? coalescing operator

Answer:
They do completely different things:

< COMPARES and returns Boolean:
  isSmaller <- 5 < 10         returns true
  isSmaller is a Boolean.

<? COMPARES and returns the LESSER VALUE:
  smaller <- 5 <? 10          returns 5
  smaller is an Integer (same type as the operands).

Side by side:
  5 < 10    returns true      (Boolean: is 5 less than 10?)
  5 <? 10   returns 5         (Integer: the lesser of 5 and 10)
  10 < 5    returns false     (Boolean: is 10 less than 5?)
  10 <? 5   returns 5         (Integer: still the lesser value)

The same pattern applies to the other coalescing operators:
  >  returns Boolean          >?  returns the GREATER value
  <= returns Boolean          <=? returns left if left <= right
  >= returns Boolean          >=? returns left if left >= right

<? is NOT less-than. It is coalescing minimum. It returns a value, not a Boolean.

See Q963 for <? in detail. See Q964 for >? in detail. See Q1050 for coalescing on classes.

defines module qa.operators.lessthanvscoalescing

  defines function

    lesserOf() as pure
      ->
        left as Integer
        right as Integer
      <- rtn as Integer: left <? right

    greaterOf() as pure
      ->
        left as Integer
        right as Integer
      <- rtn as Integer: left >? right

  defines program

    LessThanVsCoalescingDemo()
      stdout <- Stdout()

      scoreA <- 85
      scoreB <- 92

      //< returns Boolean
      isLess <- scoreA < scoreB
      stdout.println(`${scoreA} < ${scoreB} = ${isLess}`)

      //<? returns the lesser VALUE
      lesser <- lesserOf(scoreA, scoreB)
      stdout.println(`${scoreA} <? ${scoreB} = ${lesser}`)

      //>? returns the greater VALUE
      greater <- greaterOf(scoreA, scoreB)
      stdout.println(`${scoreA} >? ${scoreB} = ${greater}`)

      //They are different types of result
      stdout.println(`< gives Boolean: ${isLess}`)
      stdout.println(`<? gives Integer: ${lesser}`)

Java: Math.min(a,b) for minimum value, a < b for comparison. Python: min(a,b) vs a < b. EK9: a <? b for minimum value, a < b for comparison.

boolean coalescing comparison less minimum than value

<- rtn as Integer: Math.min(left, right)

Explanation: EK9 has no Math class; use the <? coalescing operator to get the lesser of two values. See ek9 -h E50001 for details.


Question: I get E05120 saying override is required on my operator ?. How do I fix it?

The compiler says override required on operator ? in my class

Fix E05120: override required on isSet operator

Why do I need override on operator ? when I defined it myself?

Answer:
Every EK9 class inherits a default operator ? from its base type. When you define your own operator ?, you are OVERRIDING the inherited one. The compiler requires the 'override' keyword to make this explicit.

BEFORE (E05120 error):
  operator ? as pure
    <- rtn as Boolean: name? and age?

AFTER (fixed):
  override operator ? as pure
    <- rtn as Boolean: name? and age?

The same applies when using 'default operator' alongside a manual operator ?. If you write both, the manual one overrides the default:

  default operator
  override operator ? as pure
    <- rtn as Boolean: name? and age?

The 'override' keyword is required on any operator that already exists in the parent type. For operator ?, this is always required because every type inherits it.

Operators that typically need 'override':
  override operator ?    isSet (always inherited)
  override operator $    string (if parent defines it or default operator was used)
  override operator ==   equality (if parent defines it)

See Q574 for override mechanics. See Q877 for the ? operator. See Q1055 for default vs manual operators.

defines module qa.overridemechanics.fixe05120

  defines class

    Person
      personName as String: String()
      age as Integer: 0

      default private Person()

      Person()
        ->
          personName as String
          age as Integer
        this.personName :=: personName
        this.age :=: age

      //CORRECT: override is required because ? is inherited
      override operator ? as pure
        <- rtn as Boolean: personName? and age?

      operator $ as pure
        <- rtn as String: `${personName} (${age})`

  defines program

    OverrideDemo()
      stdout <- Stdout()

      person <- Person("Alice", 30)
      stdout.println(`Is set: ${person?}`)
      stdout.println(`${person}`)

Java: @Override annotation (optional but recommended). EK9: override keyword (mandatory). The compiler enforces it — no optional about it.

E05120 class fix isSet operator override required

      operator ? as pure
        <- rtn as Boolean: name? and age?

Explanation: Add 'override' before 'operator ?'. Every class inherits operator ? from its base type, so your definition overrides it.


Question: Can I use default operator and also define my own operator ? on the same class?

How do I mix default operator with a custom isSet check?

I get E02030 duplicate operator when I use default operator and operator ?

Should I use default operator or write my own operators?

Answer:
Yes, but you must choose one of two approaches:

APPROACH 1 — default operator only (simplest):
  defines class
    Config
      host as String: String()
      port as Integer: 0
      default operator

This generates ?, $, ==, <>, <=>, #? from all fields. Use this when field-by-field behaviour is correct.

APPROACH 2 — custom override then default operator last:
  defines class
    Config
      host as String: String()
      port as Integer: 0
      override operator ? as pure
        <- rtn as Boolean: host?
      default operator

IMPORTANT: 'default operator' must be the LAST item in the class body. Put your override operators BEFORE it. The 'override' keyword is required because default operator generates ? first.

WHAT CAUSES E02030:
  default operator
  operator ? as pure           WRONG: duplicate, missing override
    <- rtn as Boolean: host?

The fix is to add 'override':
  default operator
  override operator ? as pure  CORRECT: overrides the generated one
    <- rtn as Boolean: host?

APPROACH 3 — no default, all manual:
  operator ? as pure           No override needed — nothing to override
    <- rtn as Boolean: host?
  operator $ as pure
    <- rtn as String: host

Wait — operator ? DOES need override even without default operator, because every class inherits ? from its base type. So always use 'override operator ?'.

 for E05120 fix. See Q116 for what default operator generates. See Q1055 for default vs manual.

defines module qa.overridemechanics.defaultwithoverride

  defines class

    //APPROACH 1: default operator only
    SimpleConfig
      host as String: String()
      port as Integer: 0

      SimpleConfig()
        ->
          host as String
          port as Integer
        this.host :=: host
        this.port :=: port

      default operator

    //APPROACH 2: custom override then default operator last
    SmartConfig
      host as String: String()
      port as Integer: 0

      SmartConfig()
        ->
          host as String
          port as Integer
        this.host :=: host
        this.port :=: port

      //Custom ? before default operator
      override operator ? as pure
        <- rtn as Boolean: host?

      default operator

  defines program

    DefaultOverrideDemo()
      stdout <- Stdout()

      simple <- SimpleConfig("localhost", 8080)
      stdout.println(`Simple set: ${simple?}`)
      stdout.println(`Simple: ${simple}`)

      smart <- SmartConfig("localhost", 8080)
      stdout.println(`Smart set: ${smart?}`)
      stdout.println(`Smart: ${smart}`)

      //SmartConfig only checks host, not port
      partialSmart <- SmartConfig(String(), 8080)
      stdout.println(`Partial set: ${partialSmart?}`)

Java: @Override on any method you override. EK9: override keyword mandatory on operators inherited from parent or generated by default operator.

E02030 E05120 combine default duplicate operator override

default operator with override
E02030 duplicate operator

      default operator
      operator ? as pure
        <- rtn as Boolean: host?

Explanation: When using default operator alongside a manual operator ?, add 'override' to the manual one. Default operator generates ? first, your definition overrides it.


Question: Which operators always need the override keyword in EK9?

When do I need to write override before an operator?

List all operators that require override in EK9

Answer:
The operator ? ALWAYS needs override on any class. Every class inherits operator ? from its root type, so your definition always overrides it.

ALWAYS needs override:
  override operator ? as pure    every class inherits this

Needs override when parent defines it OR default operator generates it:
  override operator $ as pure    if parent has $ or default operator was used
  override operator == as pure   if parent has == or default operator was used
  override operator <> as pure   if parent has <>
  override operator <=> as pure  if parent has <=>
  override operator #? as pure   if parent has #?

Does NOT need override (no parent version exists):
  operator $ as pure             if no parent $ and no default operator
  operator #^ as pure            promote — rarely inherited
  operator ++ / += / -=          mutation operators
  operator :=: / :~: / :^:       copy/merge/replace

Simple rule: if the compiler says E05120, add override. If it says E05110, remove override.

E05120 = override required (parent has this operator)
E05110 = override not valid (parent does NOT have this operator)

 for E05120 fix. See Q1069 for mixing default operator with overrides.

defines module qa.overridemechanics.overridealways

  defines class

    //operator ? ALWAYS needs override
    Account
      accountId as Integer: 0

      default private Account()

      Account()
        -> accountId as Integer
        this.accountId :=: accountId

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

      //operator $ does NOT need override here (no parent $)
      operator $ as pure
        <- rtn as String: `Account-${accountId}`

  defines program

    OverrideAlwaysDemo()
      stdout <- Stdout()

      acct <- Account(1001)
      stdout.println(`Set: ${acct?}`)
      stdout.println(`${acct}`)

Java: @Override is optional but recommended. EK9: override is mandatory — the compiler enforces it.

E05110 E05120 always isSet list operator override

which operators need override
override rules

      operator ? as pure
        <- rtn as Boolean: name?

Explanation: operator ? always needs 'override' because every class inherits it from its base type.


Question: Why does the compiler require two constructors for my generic class?

My generic class gets 'requires 2 constructors' error

How do I define constructors for a generic type in EK9?

Answer:
EK9 generic types require exactly two constructors: a default (no-arg) constructor and a parameterised constructor.

Why two constructors?
1. The DEFAULT constructor creates an unset instance (needed for type inference)
2. The PARAMETERISED constructor creates a set instance with values

BEFORE (E06040 error — only one constructor):
  defines class
    Pair of type (A, B)
      first as A: A()
      second as B: B()

      Pair()
        -> first as A, second as B
        this.first :=: first
        this.second :=: second

AFTER (fixed — both constructors):
  defines class
    Pair of type (A, B)
      first as A: A()
      second as B: B()

      default Pair()

      Pair()
        -> first as A, second as B
        this.first :=: first
        this.second :=: second

The default constructor must be public for generic types (E06060 prevents private). The compiler uses it internally for type inference.

See Q194 for generic class basics. See Q196 for multi-parameter generics. See Q116 for default operator.

defines module qa.generics.twoconstructors

  defines class

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

      //Constructor 1: default (used by compiler for type inference)
      default Pair()

      //Constructor 2: parameterised (public — used by code)
      Pair()
        ->
          first as A
          second as B
        this.first :=: first
        this.second :=: second

      override operator ? as pure
        <- rtn as Boolean: first? and second?

      operator $ as pure
        <- rtn as String: `(${$first}, ${$second})`

  defines program

    GenericConstructorDemo()
      stdout <- Stdout()

      pair <- Pair("Alice", 30)
      stdout.println(`Pair: ${pair}`)

Java: generics have no constructor requirements. Rust: no constructors, uses associated functions. EK9: generic types MUST have exactly 2 constructors — default (private) and parameterised.

E06040 constructor default generic private two type

E06040 fix
generic constructor requirement

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

      Pair()
        -> first as A, second as B

Explanation: Generic types require 2 constructors: a default (no-arg) and a parameterised one. Add 'default private Pair()' for the default constructor.


Question: Why does my abstract function get E08180 on the return value?

How do I declare the return value on an abstract function?

What is E08180 variable not initialised in an abstract function?

Should I use String? or String() for abstract function returns?

Answer:
Abstract function return values must be declared with ? (unset marker) or initialised with a default value.

BEFORE (E08180 error):
  transformer() as abstract
    -> input as String
    <- rtn as String

The problem: 'rtn as String' has no initial value and no ? marker. The compiler cannot guarantee it will be set.

AFTER — Option 1 (? marker — returns Optional-like):
  transformer() as abstract
    -> input as String
    <- rtn as String?

AFTER — Option 2 (default value):
  transformer() as abstract
    -> input as String
    <- rtn as String: String()

Both work. Use ? when the function might legitimately return unset. Use a default value when a fallback makes sense.

For dynamic functions implementing the abstract, the return variable inherits the declaration:
  myFn <- (prefix) is transformer as function
    rtn: prefix + input

See Q51 for abstract function types. See Q52 for dynamic functions.  for override on operators.

defines module qa.functions.abstractreturn

  defines function

    //Abstract with ? marker on return
    transformer() as abstract
      -> input as String
      <- rtn as String?

  defines program

    AbstractReturnDemo()
      stdout <- Stdout()

      //Dynamic function implementing the abstract
      prefix <- "Hello: "
      myFn <- (prefix) is transformer as function
        rtn: prefix + input

      result <- myFn("World")
      stdout.println(result)

Java: abstract methods declare return type only. Python: no return type. EK9: abstract function return variables must be ? or initialised.

E08180 abstract function initialise return unset

Explanation: Abstract function return values need ? (unset marker) or a default initialiser. Use '<- rtn as String?' to allow unset returns.


Question: How do I sort a stream in descending order in EK9?

How do I get the top N largest items from a list?

How does sort by work with a comparator function?

Answer:
EK9 has two sort forms in stream pipelines:

1. 'sort' — uses the natural ordering (ascending via <=> operator)
2. 'sort by comparatorFunction' — uses a custom comparator function

ASCENDING (natural order):
  cat scores | sort | head 3 | collect as List of Integer

DESCENDING (custom comparator):
  cat scores | sort by descending | head 3 | collect as List of Integer

The comparator function must take two parameters of the same type and return Integer:
  descending() as pure
    -> left as Integer, right as Integer
    <- rtn as Integer: right <=> left

Note: right <=> left reverses the order (compare right to left instead of left to right).

For ascending, left <=> right. For descending, right <=> left.

You can also use 'tail N' with ascending sort to get the N largest:
  cat scores | sort | tail 3 | collect as List of Integer

See Q979 for sort by examples. See Q120 for collection sorting. See Q235 for complete stream operations.

defines module qa.streams.sortdescending

  defines function

    //Descending comparator: reverse the <=> order
    descending() as pure
      ->
        left as Integer
        right as Integer
      <- rtn as Integer: right <=> left

  defines program

    SortDemo()
      stdout <- Stdout()

      scores <- [85, 42, 91, 67, 73, 55, 88, 96, 61, 79]
      stdout.println(`All: ${scores}`)

      //Top 3 largest: sort descending with comparator, take head
      topThree <- cat scores
        | sort by descending
        | head 3
        | collect as List of Integer
      stdout.println(`Top 3: ${topThree}`)

      //Bottom 3 smallest: sort ascending (natural), take head
      bottomThree <- cat scores
        | sort
        | head 3
        | collect as List of Integer
      stdout.println(`Bottom 3: ${bottomThree}`)

Java: stream().sorted(Comparator.reverseOrder()). Python: sorted(items, reverse=True). Rust: sort().rev(). EK9: sort by with reversed comparator (right <=> left).

comparator descending head pipeline reverse sort stream

descending sort
sort by comparator
stream sort


Question: How do I use a constrained type as a field in a record?

Can I put a constrained type in a record constructor?

Why does my constrained type field get a method resolution error?

How do I create a record with a Percentage field?

Answer:
Constrained types work as record fields, but the constructor parameter must match the constrained type, not the base type.

Define the constrained type:
  defines type
    Percentage as Integer constrain as
      >= 0 and <= 100

Use it in a record:
  defines record
    ExamResult
      studentName as String: String()
      score as Percentage: Percentage(0)

      ExamResult()
        -> studentName as String, score as Percentage
        this.studentName :=: studentName
        this.score :=: score

      default operator

IMPORTANT: The constructor parameter must be 'score as Percentage', not 'score as Integer'. Even though Percentage is based on Integer, they are distinct types.

In the program, create with the constrained type:
  result <- ExamResult("Alice", Percentage(85))

Not:
  result <- ExamResult("Alice", 85)    WRONG — 85 is Integer, not Percentage

See Q257 for constrained type basics. See Q717 for constrained type comparisons. See Q720 for constrainable types.

defines module qa.advancedtypes.constrainedinrecord

  defines type

    Percentage as Integer constrain as
      >= 0 and <= 100

  defines record

    ExamResult
      studentName as String: String()
      score as Percentage: Percentage(0)

      ExamResult()
        ->
          studentName as String
          score as Percentage
        this.studentName :=: studentName
        this.score :=: score

      default operator

  defines program

    ConstrainedRecordDemo()
      stdout <- Stdout()

      result <- ExamResult("Alice", Percentage(85))
      stdout.println(`${result}`)

Java: no direct equivalent. Rust: newtype pattern. Python: no constraint at type level. EK9: constrained types are distinct types — constructor must use the constrained type.

constrained field integer percentage record type

constrained type in record
constructor type matching

Explanation: 85 is an Integer, not a Percentage. Use Percentage(85) to create the constrained type value explicitly.


Question: Copy all fields from one record to another.

Transfer field values between two records using :=:

Clone a record into a second record in EK9

In Java I'd use clone() or a copy constructor. What's the EK9 equivalent?

I have two Config records and need to make the second match the first

Answer:
Copy with :=: — target gets all field values from source:

  backup <- ServerConfig()
  backup :=: original
  stdout.println(`Backup: ${backup}`)

Source is unchanged. Use 'default operator' to auto-generate :=: from fields.

See Q1058 for copy vs merge vs replace. See Q1076 for partial copies.

defines module qa.operators.copyrecorddata

  defines class

    ServerConfig
      host <- String()
      port <- Integer()
      maxConn <- Integer()

      ServerConfig()
        ->
          host as String
          port as Integer
          maxConn as Integer
        this.host :=: host
        this.port :=: port
        this.maxConn :=: maxConn

      default operator ?
      default operator $
      default operator :=:

  defines program

    CopyRecordDataDemo()
      stdout <- Stdout()

      //Create a fully configured server
      primary <- ServerConfig("db.prod.internal", 5432, 100)
      stdout.println(`Primary: ${primary}`)

      //Copy all fields to a backup config
      backup <- ServerConfig()
      backup :=: primary
      stdout.println(`Backup: ${backup}`)

      //Copy overwrites existing values too
      staging <- ServerConfig("staging.local", 3000, 10)
      stdout.println(`Staging before: ${staging}`)
      staging :=: primary
      stdout.println(`Staging after: ${staging}`)

Java: clone() or copy constructor. Python: copy.copy(). Rust: Clone trait. Go: struct assignment. EK9: :=: operator copies all fields from source to target.

:=: clone copy duplicate fields record transfer

Explanation: EK9 records have no clone() method, so primary.clone() is unresolved — use the :=: copy operator to copy all fields. See ek9 -h E50060 for details.


Question: Copy a record that has some fields unset to another record.

I have a user profile with only name and email filled in and need to duplicate it

In Java clone() copies all fields including nulls. Does EK9 :=: copy unset fields too?

Given a partially-initialised config, create an exact copy preserving the unset state

Transfer partial data between two objects using :=:

Answer:
:=: copies ALL fields including unset ones:

  partial <- UserProfile("Alice", "alice@example.com")
  copy <- UserProfile()
  copy :=: partial
  stdout.println(`Copy set?: ${copy?}`)

Phone stays unset in the copy — :=: is a faithful snapshot. To copy only SET fields, use :~: merge instead.

See Q1075 for full copy. See Q1077 for merge. See Q1058 for overview.

defines module qa.operators.copypartialrecord

  defines class

    UserProfile
      name <- String()
      email <- String()
      phone <- String()

      UserProfile()
        ->
          name as String
          email as String
        this.name :=: name
        this.email :=: email
        //phone remains unset

      default operator ?
      default operator $
      default operator :=:

  defines program

    CopyPartialRecordDemo()
      stdout <- Stdout()

      //Only name and email are set, phone is unset
      partial <- UserProfile("Alice", "alice@example.com")
      stdout.println(`Source: ${partial}`)
      stdout.println(`Source set?: ${partial?}`)

      //Copy transfers everything — including unset phone
      copy <- UserProfile()
      copy :=: partial
      stdout.println(`Copy: ${copy}`)
      stdout.println(`Copy set?: ${copy?}`)

Java: no equivalent — clone() copies all fields. Python: copy.copy() copies all. EK9 distinguishes :=: (copy all including unset) from :~: (merge only set fields).

:=: copy fields incomplete partial tri-state unset

:=: with partial data
unset field copying


Question: Merge only the set fields from one record into another.

Apply a partial update to an existing record using :~:

Patch a record with only the fields that have values

In Java I'd check each field for null before copying. What's the EK9 way?

I have an update object with some fields filled in and want to apply it to my main record

Answer:
Implement :~: to guard each field, then merge:

  operator :~:
    -> from as Address
    if from.street?
      street :=: from.street
    if from.postcode?
      postcode :=: from.postcode

  addr :~: update

Only SET fields from the source are applied. Unset fields leave the target unchanged.

See Q1075 for :=: copy. See Q1058 for overview.

defines module qa.operators.mergepartialrecord

  defines class

    Address
      street <- String()
      city <- String()
      postcode <- String()

      Address()
        ->
          street as String
          city as String
          postcode as String
        this.street :=: street
        this.city :=: city
        this.postcode :=: postcode

      //Constructor for partial updates — only postcode set
      Address()
        -> postcode as String
        this.postcode :=: postcode

      //Merge only copies SET fields from source
      operator :~:
        -> from as Address
        if from.street?
          street :=: from.street
        if from.city?
          city :=: from.city
        if from.postcode?
          postcode :=: from.postcode

      default operator ?
      default operator $
      default operator :=:

  defines program

    MergePartialRecordDemo()
      stdout <- Stdout()

      //Existing address with all fields set
      addr <- Address("10 High Street", "London", "SW1A 1AA")
      stdout.println(`Before: ${addr}`)

      //Partial update — only postcode is set via single-arg constructor
      update <- Address("EC2R 8AH")
      stdout.println(`Update: ${update}`)

      //Merge applies only the set postcode, leaves street and city alone
      addr :~: update
      stdout.println(`After merge: ${addr}`)

Java: manual null checks per field. Python: dict.update() with filtering. Go: manual field-by-field with zero-value checks. EK9: :~: merge operator with ? guards per field.

:~:
guard
merge
partial
patch
set fields only
update

:~: merge operator
partial update pattern

Explanation: EK9 has no '.merge()' method; use the ':~:' operator to merge only the set fields of a record. See ek9 -h E50060 for details.


Question: Replace all content in a List with another List.

Swap the contents of one collection with another

In Java I'd clear() then addAll(). How do I replace list contents in EK9?

I need to replace all items in a list with items from a different list

Answer:
Replace with :^: — target gets all source items:
  tasks :^: newSprint

The target list is cleared and filled with the source's items. The source is unchanged.

See Q1075 for :=: copy. See Q1077 for :~: merge.

defines module qa.operators.replacerecordcontents

  defines class

    Task
      name <- String()
      priority <- Integer()

      Task()
        ->
          name as String
          priority as Integer
        this.name :=: name
        this.priority :=: priority

      default operator

  defines program

    ReplaceRecordContentsDemo()
      stdout <- Stdout()

      //Current task list
      tasks <- List() of Task
      tasks += Task("Fix login bug", 1)
      tasks += Task("Update docs", 3)
      stdout.println(`Current tasks: ${tasks}`)

      //New sprint tasks to replace the current list entirely
      newSprint <- List() of Task
      newSprint += Task("Deploy v2.0", 1)
      newSprint += Task("Performance audit", 2)
      newSprint += Task("Add monitoring", 3)

      //Replace all current tasks with the new sprint
      tasks :^: newSprint
      stdout.println(`After replace: ${tasks}`)

Java: list.clear(); list.addAll(other). Python: target[:] = source. Go: target = make([]T, len(source)); copy(target, source). EK9: target :^: source replaces contents in place.

:^:
collection
list
overwrite
replace
swap contents

:^: replace on collections
list content replacement

Explanation: EK9 List has no 'addAll' method — use the ':^:' replace operator to overwrite a collection's contents in place. See ek9 -h E50060 for details.


Question: Duplicate an endpoint config, then apply a partial timeout update to it.

I need to back up a config with :=: then patch one field using :~:

In Python I'd copy then dict.update(). Write the EK9 equivalent with :=: and :~:

Given an Endpoint, create an exact copy then merge in a timeout-only change

Copy all fields from one record, then merge only the set fields from a patch

Answer:
Copy all fields, then merge a partial update:
  copied :=: original
  target :~: patch

:=: copies ALL fields (set and unset). :~: copies only SET fields, leaving unset fields in the target unchanged.

See Q1075 for copy details. See Q1077 for merge details.

defines module qa.operators.copyvsmergereplace

  defines class

    Endpoint
      url <- String()
      timeout <- Integer()
      retries <- Integer()

      Endpoint()
        ->
          url as String
          timeout as Integer
          retries as Integer
        this.url :=: url
        this.timeout :=: timeout
        this.retries :=: retries

      //Constructor for partial updates — only timeout
      Endpoint()
        -> timeout as Integer
        this.timeout :=: timeout

      operator :~:
        -> from as Endpoint
        if from.url?
          url :=: from.url
        if from.timeout?
          timeout :=: from.timeout
        if from.retries?
          retries :=: from.retries

      default operator ?
      default operator $
      default operator :=:

  defines program

    CopyVsMergeReplaceDemo()
      stdout <- Stdout()

      original <- Endpoint("https://api.example.com", 30, 3)

      // :=: COPY — exact duplicate of all fields
      copied <- Endpoint()
      copied :=: original
      stdout.println(`Copy: ${copied}`)

      // :~: MERGE — only the set timeout field is applied
      patch <- Endpoint(60)
      target <- Endpoint("https://old.example.com", 10, 1)
      stdout.println(`Before merge: ${target}`)
      target :~: patch
      stdout.println(`After merge: ${target}`)

Java: no distinction — clone() copies all. Python: copy vs dict.update(). Go: struct assignment vs manual field merge. EK9: :=: (duplicate all) vs :~: (patch set fields only).

:=:
:~:
comparison
copy
difference
merge
partial update


Question: Copy a child class including parent fields.

I have a Car that extends Vehicle and need to copy all fields including make and year

In Java I'd call super.clone() in a subclass. Write the EK9 equivalent

Given a class hierarchy with Vehicle and Car, duplicate a Car preserving parent fields

Transfer all data from one extended object to another using :=:

Answer:
Copy an inherited type with :=: — parent and child fields included:
  dst :=: src

Both parent and child must declare 'default operator :=:'. The parent must be declared 'as open' to allow extension.

See Q1075 for basic copy.

defines module qa.operators.copywithinheritance

  defines class

    Vehicle as open
      make <- String()
      year <- Integer()

      Vehicle()
        ->
          make as String
          year as Integer
        this.make :=: make
        this.year :=: year

      default operator ?
      default operator $
      default operator :=:

    Car extends Vehicle
      doors <- Integer()

      Car()
        ->
          make as String
          year as Integer
          doors as Integer
        super(make, year)
        this.doors :=: doors

      default operator ?
      default operator $
      default operator :=:

  defines program

    CopyWithInheritanceDemo()
      stdout <- Stdout()

      //Source has parent fields (make, year) and child field (doors)
      src <- Car("Toyota", 2024, 4)
      stdout.println(`Source: ${src}`)

      //Copy transfers all fields including inherited ones
      dst <- Car()
      dst :=: src
      stdout.println(`Copy: ${dst}`)
      stdout.println(`Copy set?: ${dst?}`)

Java: clone() with super.clone(). Python: copy.deepcopy() handles inheritance. Go: embed + struct copy. EK9: default operator :=: on both parent and child handles the chain automatically.

:=: child copy extends inheritance parent super

:=: with inheritance
copying inherited fields

Explanation: The parent class must be declared 'as open' to allow extension — EK9 types are closed by default. See ek9 -h E05030 for details.


Question: Merge two lists together using :~: operator.

Add all elements from one list into another list

In Java I'd use addAll(). What is the EK9 equivalent?

I have two lists and need to merge the second into the first

Answer:
Merge one list into another with :~::
  team :~: newHires

All elements from the source are added to the end of the target. The source is unchanged.

See Q1058 for copy vs merge vs replace.

defines module qa.operators.mergecollection

  defines program

    MergeCollectionDemo()
      stdout <- Stdout()

      //Two separate lists of names
      team <- ["Alice", "Bob", "Charlie"]
      newHires <- ["Diana", "Eve"]
      stdout.println(`Team: ${team}`)
      stdout.println(`New hires: ${newHires}`)

      //Merge new hires into the team list
      team :~: newHires
      stdout.println(`After merge: ${team}`)

      //Merge a single element
      team :~: "Frank"
      stdout.println(`After single merge: ${team}`)

Java: list.addAll(other). Python: list.extend(other). Go: append(slice, other...). EK9: list :~: otherList or list += otherList.

:~: addAll append collection combine list merge

Explanation: EK9's List has no addAll method — use the merge operator ':~:' to add all elements of one list into another. See ek9 -h E50060 for details.


Question: Compare two Product records by price.

Write code that checks which of two records is greater

Determine ordering between two objects using <=>

In Java I'd use Comparable.compareTo(). What is the EK9 equivalent?

I have two products and need to know which one costs more

Answer:
Compare two records with <=> and use the derived operators:
  cmp <- itemA <=> itemB
  if itemA > itemB
    stdout.println("A is greater")

'default operator' generates <=> and all derived comparison operators (==, <>, <, >, <=, >=).

See Q1085 for sorting records. See Q1083 for equality checking.

defines module qa.operators.comparetworecords

  defines class

    Product
      name <- String()
      price <- Float()

      Product()
        ->
          name as String
          price as Float
        this.name :=: name
        this.price :=: price

      default operator

  defines program

    CompareRecordsDemo()
      stdout <- Stdout()

      itemA <- Product("Keyboard", 49.99)
      itemB <- Product("Mouse", 29.99)

      cmp <- itemA <=> itemB
      stdout.println(`Compare: ${cmp}`)

      if itemA > itemB
        stdout.println(`${itemA} costs more`)
      else
        stdout.println(`${itemB} costs more`)

      if itemA == itemB
        stdout.println("Same product")
      else
        stdout.println("Different products")

Java: implements Comparable<T>, compareTo(). Python: __lt__, __eq__ or functools.total_ordering. Rust: impl Ord. Go: manual comparison. EK9: default operator <=> or custom operator <=>.

<=> compare comparison greater less ordering record

Explanation: EK9 has no .compareTo() method; use the '<=>' operator for three-way comparison. See ek9 -h E50060 for details.


Question: Check if two Config records have identical values.

Test equality between two objects using ==

Determine if two records contain the same data

In Java I'd use .equals(). How do I check equality in EK9?

I have two config objects and need to verify they match

Answer:
Check equality and inequality with == and <>:
  if primary == replica
    stdout.println("match")
  if primary <> staging
    stdout.println("differ")

'default operator' generates == and <> from the <=> comparison. Both objects must be SET for meaningful comparison.

See Q1082 for ordering comparison.

defines module qa.operators.recordequality

  defines class

    Config
      host <- String()
      port <- Integer()

      Config()
        ->
          host as String
          port as Integer
        this.host :=: host
        this.port :=: port

      default operator

  defines program

    RecordEqualityDemo()
      stdout <- Stdout()

      primary <- Config("db.prod", 5432)
      replica <- Config("db.prod", 5432)
      staging <- Config("db.stage", 5432)

      //Equal values
      if primary == replica
        stdout.println("Primary and replica match")

      //Different values
      if primary <> staging
        stdout.println("Primary and staging differ")

Java: .equals() and Objects.equals(). Python: == (with __eq__). Rust: PartialEq. Go: reflect.DeepEqual or manual. EK9: default operator == or custom operator ==.

<> == compare equal equality identical match

Explanation: EK9 has no .equals() method; use the == operator for equality and <> for inequality. See ek9 -h E50060 for details.


Question: Guard a comparison result when one sensor reading might be unset.

I have two sensor objects and one might be uninitialised — compare them safely

In Java this would throw NullPointerException. Write the safe EK9 comparison

Given an active and an offline sensor, compare them and handle the unset case

Check whether a <=> comparison result is valid before using it

Answer:
Guard a comparison result when an operand might be unset:
  cmp <- active <=> offline
  if cmp?
    stdout.println($cmp)

When either operand is unset, <=> produces an unset result. Always guard with ? before using the value.

See Q1082 for normal comparison. See Q1087 for checking if records are set.

defines module qa.operators.compareunsetrecords

  defines class

    Sensor
      name <- String()
      reading <- Float()

      Sensor()
        ->
          name as String
          reading as Float
        this.name :=: name
        this.reading :=: reading

      default operator

  defines program

    CompareUnsetRecordsDemo()
      stdout <- Stdout()

      active <- Sensor("Temperature", 23.5)
      offline <- Sensor()
      stdout.println(`Active: ${active}`)
      stdout.println(`Offline set?: ${offline?}`)

      //Compare set with unset — result is unset
      cmp <- active <=> offline
      if cmp?
        stdout.println(`Comparison: ${cmp}`)
      else
        stdout.println("Comparison unset: cannot compare with unset sensor")

Java: NullPointerException if null. Python: TypeError on None comparison. Go: panic on nil. EK9: comparison with unset object returns unset result — no crash, no wrong answer.

<=>
absent
check result
compare
guard
tri-state
unset

comparison with unset objects
tri-state comparison

Explanation: A <=> comparison can be unset, so branch on the guarded result `if cmp?` (a Boolean); branching on the raw Integer `if cmp` is not Boolean-compatible and triggers E07540. See ek9 -h E07540 for details.


Question: Sort a list of Employee records by salary.

Order objects in a list using a stream sort pipeline

Arrange records from lowest to highest value

In Java I'd use Collections.sort with Comparator.comparing(). How in EK9?

I have a list of employees and need to sort them by pay

Answer:
Sort records via stream pipeline using default <=>:
  sorted <- cat staff | sort | collect as List of Employee

'default operator' generates <=> across all fields in declaration order. For field-specific sorting, implement a custom <=>.

See Q1073 for descending sort. See Q1082 for basic comparison.

defines module qa.operators.sortrecordsbyfield

  defines class

    Employee
      name <- String()
      salary <- Float()

      Employee()
        ->
          name as String
          salary as Float
        this.name :=: name
        this.salary :=: salary

      default operator

  defines program

    SortRecordsByFieldDemo()
      stdout <- Stdout()

      staff <- List() of Employee
      staff += Employee("Alice", 75000.0)
      staff += Employee("Bob", 55000.0)
      staff += Employee("Charlie", 92000.0)
      staff += Employee("Diana", 68000.0)

      stdout.println("Unsorted:")
      cat staff > stdout

      //Sort by natural ordering (fields in declaration order: name then salary)
      sorted <- cat staff | sort | collect as List of Employee
      stdout.println("Sorted:")
      cat sorted > stdout

Java: Collections.sort(list, Comparator.comparing(Employee::getSalary)). Python: sorted(employees, key=lambda e: e.salary). Rust: sort_by_key(). EK9: cat list | sort | collect as List of T.

ascending list order pipeline record sort stream

Explanation: EK9 List has no .sort() method; sort via a stream pipeline 'cat list | sort | collect as List of T'. See ek9 -h E50060 for details.


Question: Find the cheapest product in a list.

Get the minimum item from a collection using a stream

Extract the smallest record from a sorted list

In Python I'd use min(). How do I find the minimum in EK9?

I have a list of products and need the one with the lowest price

Answer:
Find the minimum with sort ascending + head 1:
  cheapest <- cat products | sort | head 1 | collect as List of Product

Sort + head 1 is the standard min pattern. For the maximum, use a descending comparator with 'sort by'.

See Q1085 for sorting records. See Q1073 for descending sort.

defines module qa.operators.findcheapestproduct

  defines class

    Product
      name <- String()
      price <- Float()

      Product()
        ->
          name as String
          price as Float
        this.name :=: name
        this.price :=: price

      default operator

  defines function

    descending() as pure
      ->
        left as Product
        right as Product
      <- rtn as Integer: right <=> left

  defines program

    FindCheapestProductDemo()
      stdout <- Stdout()

      products <- List() of Product
      products += Product("Keyboard", 49.99)
      products += Product("Mouse", 19.99)
      products += Product("Monitor", 299.99)
      products += Product("Cable", 9.99)

      //Find cheapest: sort ascending, take first
      cheapest <- cat products | sort | head 1 | collect as List of Product
      stdout.println(`Cheapest: ${cheapest}`)

      //Find most expensive: sort descending, take first
      priciest <- cat products | sort by descending | head 1 | collect as List of Product
      stdout.println(`Priciest: ${priciest}`)

Java: Collections.min(list, comparator). Python: min(products, key=lambda p: p.price). Rust: iter().min_by_key(). EK9: cat list | sort | head 1 | collect.

cheapest find head minimum smallest sort stream

Explanation: EK9 has no .min() method. Use a stream pipeline: sort ascending, then head 1 to get the smallest.


Question: Check if all fields in a record have been set.

Test whether an object is fully initialised using ?

Determine if a record has any unset fields

In Java I'd check each field for null. How do I check if an EK9 object is set?

I need to verify that a config object has all required values before using it

Answer:
Check if a record is set with the ? operator:
  if full?
    stdout.println("set")

'default operator ?' uses ANY-field semantics — returns true if ANY field is set. To require ALL fields, use 'override operator ?'.

See Q1089 for custom override operator ?.

defines module qa.operators.checkrecordfullyset

  defines class

    AppConfig
      dbHost <- String()
      dbPort <- Integer()
      apiKey <- String()

      AppConfig()
        ->
          dbHost as String
          dbPort as Integer
          apiKey as String
        this.dbHost :=: dbHost
        this.dbPort :=: dbPort
        this.apiKey :=: apiKey

      AppConfig()
        -> dbHost as String
        this.dbHost :=: dbHost

      default operator

  defines program

    CheckRecordFullySetDemo()
      stdout <- Stdout()

      //Fully set config
      full <- AppConfig("db.prod", 5432, "secret-key-123")
      stdout.println(`Full config set?: ${full?}`)

      //Partially set — only dbHost provided
      partial <- AppConfig("db.dev")
      stdout.println(`Partial config set?: ${partial?}`)

      //Empty — no fields set
      blank <- AppConfig()
      stdout.println(`Empty config set?: ${blank?}`)

Java: manual null checks per field, or Bean Validation @NotNull. Python: all(v is not None for v in vars(obj).values()). EK9: ? operator with default (any-field) or custom (all-field) semantics.

? check initialised isSet operator set validate

Explanation: EK9 has no 'null'; use the '?' operator to test whether an object is set. See ek9 -h E01073 for details.


Question: Check which fields are set in a partially-initialised record.

I have a contact with only a name filled in — verify that ? still returns true

In Java I'd check each field for null separately. Show the EK9 ANY-field semantics

Given an object with 1 of 3 fields set, confirm the default operator ? result

Test whether a partially-initialised object counts as set in EK9

Answer:
A partially-initialised object is still 'set' under default ?:
  partial <- ContactInfo("Alice")
  if partial?
    stdout.println("set")

With 1 of 3 fields set, default ? returns true (ANY-field semantics).

See Q1087 for basic isSet. See Q1089 for custom override operator ?.

defines module qa.operators.checkpartialset

  defines class

    ContactInfo
      name <- String()
      email <- String()
      phone <- String()

      ContactInfo()
        -> name as String
        this.name :=: name
        //email and phone remain unset

      default operator

  defines program

    CheckPartialSetDemo()
      stdout <- Stdout()

      //Only name is set — email and phone remain unset
      partial <- ContactInfo("Alice")
      stdout.println(`Contact: ${partial}`)

      //default ? uses ANY-field semantics: 1 of 3 is set, so true
      stdout.println(`Is set?: ${partial?}`)

      //Compare with fully unset object
      blank <- ContactInfo()
      stdout.println(`Empty set?: ${blank?}`)

Java: check each field != null individually. Python: hasattr or getattr checks. EK9: default ? checks any field; override ? for custom logic.

? check field individual isSet partial unset

Explanation: 'null' does not exist in EK9 — check an object's overall set-status with the '?' operator (partial?) instead of comparing to null. See ek9 -h E01073 for details.


Question: Write a custom override operator ? for a class with validation logic.

I need my Registration to be considered valid only when name, email, and age are all present

In Java I'd write a custom isValid() method. Write the EK9 equivalent with override operator ?

Given a class with three required fields, make operator ? enforce all-field checking

Implement an override operator ? that requires ALL fields to be set before the object is usable

Answer:
Override ? before default operator to require all fields:
  override operator ? as pure
    <- rtn as Boolean: name? and email? and age?
  default operator

The override keyword is mandatory — every class inherits ? from its base type. 'default operator' must be the LAST item in the class body.

See Q1069 for mixing default with overrides. See Q1070 for which operators need override.

defines module qa.operators.overrideoperatorisset

  defines class

    Registration
      name <- String()
      email <- String()
      age <- Integer()

      Registration()
        ->
          name as String
          email as String
          age as Integer
        this.name :=: name
        this.email :=: email
        this.age :=: age

      Registration()
        -> name as String
        this.name :=: name

      //Custom ?: require ALL fields before default operator
      override operator ? as pure
        <- rtn as Boolean: name? and email? and age?

      default operator

  defines program

    OverrideOperatorIsSetDemo()
      stdout <- Stdout()

      //Fully set — override ? returns true
      complete <- Registration("Bob", "bob@example.com", 30)
      stdout.println(`Complete set?: ${complete?}`)

      //Partially set — override ? returns false (not all fields set)
      partial <- Registration("Alice")
      stdout.println(`Partial set?: ${partial?}`)

Java: custom isValid() method. Python: __bool__ override. Rust: custom is_valid(). EK9: override operator ? before default operator.

all fields
custom
isSet
operator
override
validate

override operator ?
custom isSet validation


Question: Define a PaymentRequest that is only 'set' when all three fields are present.

I need my payment object to reject partial data — override operator ? to require all fields

In Java I'd write an isValid() method. Write the EK9 equivalent using override operator ?

Given a class with amount, currency, and recipient, make ? return false unless all are set

Create a class where default operator generates most operators but ? has custom all-field logic

Answer:
Custom ? goes before default operator — require all fields:
  override operator ? as pure
    <- rtn as Boolean: amount? and currency? and recipient?
  default operator

Use 'default operator' alone for ANY-field semantics. Add 'override operator ?' before it when ALL fields are required.

See Q1069 for mixing default with overrides. See Q1087 for default ?.

defines module qa.operators.defaultvsoverrideisset

  defines class

    //ALL required fields needed for a valid payment
    PaymentRequest
      amount <- Float()
      currency <- String()
      recipient <- String()

      PaymentRequest()
        ->
          amount as Float
          currency as String
          recipient as String
        this.amount :=: amount
        this.currency :=: currency
        this.recipient :=: recipient

      PaymentRequest()
        -> amount as Float
        this.amount :=: amount

      //Custom ?: ALL fields required — BEFORE default operator
      override operator ? as pure
        <- rtn as Boolean: amount? and currency? and recipient?

      default operator

  defines program

    DefaultVsOverrideIsSetDemo()
      stdout <- Stdout()

      //ALL fields set — override ? returns true
      validPayment <- PaymentRequest(99.99, "GBP", "Alice")
      stdout.println(`Valid payment set?: ${validPayment?}`)

      //Only amount set — override ? returns false
      partialPayment <- PaymentRequest(50.00)
      stdout.println(`Partial payment set?: ${partialPayment?}`)

Java: no equivalent — isValid() is manual. Python: __bool__ is one semantics. EK9: default ? (any-field) + optional override ? (custom logic).

? all-field any-field choose default isSet override semantics

default vs override operator ?
isSet design decision

      default operator

      override operator ? as pure
        <- rtn as Boolean: x? and y?

Explanation: default operator must be the LAST item in the class body. Put override operator ? BEFORE default operator.


Question: Provide a fallback display name when the user name is unset.

I have an optional user name and need to show 'Guest' if it is missing

In Kotlin I'd use the ?: elvis operator. Write the EK9 equivalent

Given a possibly-unset string, return it if set or a default otherwise

Supply a default value for an unset variable using ?: coalescing

Answer:
Elvis coalescing:
  displayName <- userName ?: "Guest"

Returns left if set, right otherwise. ?: and ?? are synonyms. See Q899 for all coalescing operators, Q1092 for coalescing min/max.

defines module qa.operators.elviscoalescing

  defines program

    ElvisCoalescingDemo()
      stdout <- Stdout()

      //User name is unset — fallback to "Guest"
      userName <- String()
      displayName <- userName ?: "Guest"
      stdout.println(`Display: ${displayName}`)

      //User name is set — use the actual value
      knownUser <- "Alice"
      displayName2 <- knownUser ?: "Guest"
      stdout.println(`Display: ${displayName2}`)

Kotlin: value ?: default. JavaScript: value ?? default. Swift: value ?? default. C#: value ?? default. EK9: value ?: default or value ?? default.

?: ?? coalescing default elvis fallback unset

Explanation: EK9 has no null; use tri-state (unset/set) values with the ?: coalescing operator for a fallback. See ek9 -h E01073 for details.


Question: Get the smaller of two prices, even if one might be unset.

I have two price values and one could be missing — find the minimum safely

In Java I'd need null checks before Math.min(). Write the safe EK9 version

Given two optional sensor readings, return the lower one using <? coalescing

Select the minimum of two values with automatic unset handling

Answer:
Coalescing min (<?) returns the smaller value, max (>?) returns the larger:

  cheapest <- priceA <? priceB
  topScore <- scoreA >? scoreB

  //With one unset — returns the set one
  knownPrice <- 49.99
  unknownPrice <- Float()
  best <- knownPrice <? unknownPrice
  //best is 49.99 — the set value wins

<? and >? are unique to EK9. If both are set, returns the smaller/larger. If one is unset, returns the set one. If both unset, result is unset.

See Q899 for all coalescing operators. See Q1091 for ?: elvis.

defines module qa.operators.minmaxcoalescing

  defines function

    cheaperOf() as pure
      ->
        left as Float
        right as Float
      <- rtn as Float: left <? right

    higherOf() as pure
      ->
        left as Integer
        right as Integer
      <- rtn as Integer: left >? right

  defines program

    MinMaxCoalescingDemo()
      stdout <- Stdout()

      //Both set — returns the smaller
      cheapest <- cheaperOf(29.99, 19.99)
      stdout.println(`Cheapest: ${cheapest}`)

      //One unset — returns the set one
      knownPrice <- 49.99
      unknownPrice <- Float()
      best <- knownPrice <? unknownPrice
      stdout.println(`Best available: ${best}`)

      //Coalescing maximum — both set, returns the larger
      topScore <- higherOf(85, 92)
      stdout.println(`Top score: ${topScore}`)

Java: a == null ? b : b == null ? a : Math.min(a, b). Python: min(x for x in [a, b] if x is not None). EK9: a <? b — one operator handles all cases.

<? >? coalescing maximum minimum safe unset

<? coalescing minimum
>? coalescing maximum

Explanation: EK9 has no Math type, so Math.min(...) cannot be resolved — use the <? coalescing minimum operator instead. See ek9 -h E50001 for details.


Question: Chain multiple fallback values to find the first one that is set.

I have three config sources and need the first available value from any of them

In JavaScript I'd chain ?? operators. Write the EK9 multi-level fallback

Given primary, secondary, and default values, return the first set one

Cascade through multiple optional values using chained ?: operators

Answer:
Chained fallback:
  port <- envPort ?: configPort ?: defaultPort

Evaluates left-to-right, returning the first set value in the chain. Works with any number of levels. See Q1091 for basic ?: coalescing, Q899 for all coalescing operators.

defines module qa.operators.chainedcoalescing

  defines program

    ChainedCoalescingDemo()
      stdout <- Stdout()

      //First two unset — falls through to the third
      envPort <- Integer()
      configPort <- Integer()
      defaultPort <- 8080

      port <- envPort ?: configPort ?: defaultPort
      stdout.println(`Port: ${port}`)

      //Second is set — stops there
      envHost <- String()
      configHost <- "config.local"
      defaultHost <- "localhost"

      host <- envHost ?: configHost ?: defaultHost
      stdout.println(`Host: ${host}`)

JavaScript: a ?? b ?? c. Kotlin: a ?: b ?: c. Swift: a ?? b ?? c. EK9: a ?: b ?: c — identical chaining syntax.

?: cascade chained coalescing fallback levels multiple

Explanation: EK9 has no 'null' - using it (here as a fallback value) is rejected with E01073; chain ?: over real set/unset values instead. See ek9 -h E01073 for details.


Question: Convert a Customer record to both display string and JSON format.

I need to print a record as a readable string and also serialise it to JSON

In Java I'd override toString() and write a toJson(). Write the EK9 equivalent

Given a class with name and email, produce $ string and $$ JSON representations

Generate both human-readable and JSON output from a single record

Answer:
String and interpolation:
  str <- $cust
  stdout.println(`Customer: ${cust}`)

Default operator $ generates ClassName(field=value) format. Works directly in backtick interpolation. See Q908 for $ string operator, Q887 for $$ JSON operator.

defines module qa.operators.recordtostringjson

  defines class

    Customer
      name <- String()
      email <- String()
      age <- Integer()

      Customer()
        ->
          name as String
          email as String
          age as Integer
        this.name :=: name
        this.email :=: email
        this.age :=: age

      default operator

  defines program

    RecordToStringJsonDemo()
      stdout <- Stdout()

      cust <- Customer("Alice", "alice@example.com", 30)

      //$ string representation
      str <- $cust
      stdout.println(`String: ${str}`)

      //In backtick interpolation
      stdout.println(`Customer: ${cust}`)

Java: toString() and manual JSON. Python: __str__ and json.dumps(). Rust: Display + serde. EK9: default operator $ and default operator $$ — one line each.

$ $$ JSON convert display serialise string

Explanation: EK9 has no Java-style '.toString()' method; use the '$' prefix operator for string conversion. See ek9 -h E50060 for details.


Question: Use a Coordinate record as a Dict key.

I need to store values in a Dict keyed by a custom class — what operators are required?

In Java I'd implement hashCode() and equals(). Write the EK9 equivalent for Dict keys

Given a Coordinate class, set up the operators needed to use it as a Dict key

Make a custom type usable as a dictionary key with #? and == operators

Answer:
Using a record as a Dict key:
  grid <- Dict() of (Coordinate, String)
  grid += DictEntry(Coordinate(0, 0), "origin")

Dict keys need #? (hashcode) and == (equality). Use 'default operator' to generate both from declared fields, ensuring the hash contract holds automatically. See Q966 for hashcode details, Q1083 for equality.

defines module qa.operators.recordasdictkey

  defines class

    Coordinate
      x <- Integer()
      y <- Integer()

      Coordinate()
        ->
          x as Integer
          y as Integer
        this.x :=: x
        this.y :=: y

      default operator

  defines program

    RecordAsDictKeyDemo()
      stdout <- Stdout()

      //Use Coordinate as Dict key — needs (K, V) parentheses
      grid <- Dict() of (Coordinate, String)
      grid += DictEntry(Coordinate(0, 0), "origin")
      grid += DictEntry(Coordinate(1, 0), "east")
      grid += DictEntry(Coordinate(0, 1), "north")

      stdout.println(`Grid entries: ${grid.length()}`)

      //Look up by equal key using getOrDefault
      lookup <- Coordinate(1, 0)
      result <- grid.getOrDefault(lookup, "unknown")
      stdout.println(`Found: ${result}`)

Java: implement hashCode() and equals(). Python: __hash__ and __eq__. Rust: Hash + PartialEq. EK9: default operator generates #? and == from fields.

Dict key requirements
#? and == for Dict keys

      hashCode()
        <- rtn as Integer: x * 31 + y

Explanation: EK9 has no hashCode() method. Use 'default operator #?' or 'default operator' to generate hashcode from fields.


Question: Verify that two equal objects produce the same hashcode.

I need to confirm the hash contract holds for my custom class

In Java I'd test that hashCode() is consistent with equals(). Show the EK9 equivalent

Given two objects with identical field values, check their #? hashcodes match

Test hashcode consistency between equal objects using #?

Answer:
Hashcode prefix operator:
  h1 <- #?p1
  h2 <- #?p2
  assert h1 == h2

Equal objects must have equal hashes. 'default operator' generates #? and == from the same fields, ensuring consistency. See Q966 for hashcode details, Q1095 for using records as Dict keys.

defines module qa.operators.hashcodeoperator

  defines class

    Point
      x <- Integer()
      y <- Integer()

      Point()
        ->
          x as Integer
          y as Integer
        this.x :=: x
        this.y :=: y

      default operator

  defines program

    HashcodeOperatorDemo()
      stdout <- Stdout()

      p1 <- Point(10, 20)
      p2 <- Point(10, 20)
      p3 <- Point(99, 20)

      //Equal objects must have equal hashcodes
      h1 <- #?p1
      h2 <- #?p2
      stdout.println(`p1 hash: ${h1}`)
      stdout.println(`p2 hash: ${h2}`)

      if h1 == h2
        stdout.println("Hash contract holds: equal objects, equal hashes")

      //Different objects likely have different hashes
      h3 <- #?p3
      stdout.println(`p3 hash: ${h3}`)

Java: obj.hashCode(). Python: hash(obj). Rust: Hash trait. EK9: #?obj prefix operator — no method call needed.

#? consistency contract equal hash hashcode

Explanation: EK9 uses the '#?' prefix operator for hashcodes, not a '.hashCode()' method — 'p1.hashCode()' does not resolve. See ek9 -h E50060 for details.


Question: Create a Product class with full operator support in one line.

I need a class with copy, compare, equals, string, hash — generate them all automatically

In Kotlin I'd use a data class. Write the EK9 equivalent with default operator

Given a class with name and price fields, add all operators without writing each one

Set up a complete class with 'default operator' to generate everything

Answer:
One line generates all operators:
  default operator

Must be the last item in the class body. Generates ==, <>, <, >, <=, >=, <=>, $, $$, #?, :=:, :~:, :^:, ? from declared fields. See Q949 for default operator rules, Q1098 for overriding individual operators after default.

defines module qa.operators.definerecorddefaultops

  defines class

    Product
      name <- String()
      price <- Float()

      Product()
        ->
          name as String
          price as Float
        this.name :=: name
        this.price :=: price

      //One line generates all operators
      default operator

  defines program

    DefineRecordDefaultOpsDemo()
      stdout <- Stdout()

      a <- Product("Widget", 9.99)
      b <- Product("Widget", 9.99)
      c <- Product("Gadget", 29.99)

      //$ string
      stdout.println(`Product: ${a}`)

      //== equality
      if a == b
        stdout.println("a and b are equal")

      //<=> comparison
      if a < c
        stdout.println("Widget is cheaper than Gadget")

      //:=: copy
      backup <- Product()
      backup :=: a
      stdout.println(`Backup: ${backup}`)

      //#? hashcode
      stdout.println(`Hash: ${#?a}`)

Kotlin: data class Product(val name: String, val price: Float). Java: record Product(String name, float price). EK9: class with 'default operator' as last line.

all
automatic
default operator
generate
one line
operators

      default operator

      someMethod() as pure
        <- rtn as String: "oops"

Explanation: 'default operator' must be the LAST item in a class body — no methods or fields may follow it. See ek9 -h E01086 for details.


Question: Generate all operators with default, but customise the string representation.

I want default operator for everything except $ which I need to customise

In Java I'd auto-generate equals/hashCode but override toString(). Write the EK9 way

Given a child class, override the parent's default $ with a custom format

Use default operator for most operators but provide a custom override operator $

Answer:
Override before default:
  override operator $ as pure
    <- rtn as String: `Custom(age=${age})`
  default operator

Overrides go BEFORE 'default operator', which must be last in the class body. See Q1069 for mixing default with overrides, Q1097 for default operator basics.

defines module qa.operators.overrideafterdefault

  defines class

    Base as open
      name as String: String()

      Base()
        -> name as String
        this.name :=? name

      default operator

    Child extends Base
      age as Integer: Integer()

      Child()
        ->
          childName as String
          age as Integer
        super(childName)
        this.age :=? age

      //Custom $ BEFORE default operator
      override operator $ as pure
        <- rtn as String: `Child(age=${age})`

      default operator

  defines program

    OverrideAfterDefaultDemo()
      stdout <- Stdout()

      base <- Base("ParentObj")
      stdout.println($base)

      child <- Child("ChildObj", 10)
      stdout.println($child)

Java: Lombok @Data + manual toString(). Kotlin: data class + override fun toString(). EK9: override operator $ before default operator.

$
combine
customise
default operator
override
string

override before default operator
custom $ with default

      default operator
      override operator $ as pure
        <- rtn as String: `Custom`

Explanation: default operator must be LAST. Put override operators BEFORE it.


Question: Check if a greeting string contains the word 'world'.

I need to test whether a string includes a specific substring

In Java I'd use str.contains(). Write the EK9 substring check

Given a message string, verify it contains a keyword and also check for absence

Search for a substring inside a string using the contains operator

Answer:
Substring check:
  hasWorld <- greeting contains "world"
  noXyz <- greeting not contains "xyz"

Keyword operator, not a method call. Returns Boolean. See Q1101 for 'is in' collection membership.

defines module qa.operators.containsnotcontains

  defines program

    ContainsNotContainsDemo()
      stdout <- Stdout()

      greeting <- "hello world"

      //Check substring is present
      hasWorld <- greeting contains "world"
      stdout.println(`Contains 'world': ${hasWorld}`)

      //Check substring is absent
      noXyz <- greeting not contains "xyz"
      stdout.println(`Not contains 'xyz': ${noXyz}`)

Java: str.contains(sub). Python: sub in str. Rust: str.contains(sub). Go: strings.Contains(). EK9: str contains sub — keyword, not method.

check
contains
not contains
search
string
substring


Question: Check if an email string matches a basic pattern.

I need to validate a string against a regex pattern in EK9

In Python I'd use re.match(). Write the EK9 regex check

Given a user input string, test it against a regular expression

Validate string format using the matches operator and a regex literal

Answer:
Regex matching:
  valid <- email matches /[a-zA-Z]+@[a-zA-Z]+\.[a-zA-Z]+/

Regex literals use /slashes/. Returns Boolean. Use 'not matches' for negation. See Q1099 for contains (substring), Q1101 for is in (collection membership).

defines module qa.operators.matchesregex

  defines program

    MatchesRegexDemo()
      stdout <- Stdout()

      email <- "alice@example.com"

      //Match against a basic email pattern
      valid <- email matches /[a-zA-Z]+@[a-zA-Z]+\.[a-zA-Z]+/
      stdout.println(`Valid email: ${valid}`)

      //Non-matching input
      badInput <- "not-an-email"
      invalid <- badInput matches /[a-zA-Z]+@[a-zA-Z]+\.[a-zA-Z]+/
      stdout.println(`Bad input valid: ${invalid}`)

Java: Pattern.matches(regex, str). Python: re.match(pattern, str). Rust: Regex::new(pat).is_match(str). EK9: str matches /pattern/ — inline regex literal.

format
matches
pattern
regex
regular expression
validate

Explanation: 'matches' takes a RegEx literal (/pattern/), so calling email.matches("...") with a String argument does not resolve. See ek9 -h E50060 for details.


Question: Check if 'banana' is in a list of approved fruits.

I have a list of allowed values and need to test membership

In Python I'd use 'if item in list'. Write the EK9 equivalent

Given a list of strings, check whether a specific value is present

Test collection membership using the 'is in' expression

Answer:
Collection membership:
  found <- "banana" is in fruits

Reverse of 'contains' (which checks substrings). Use 'not in' for negation. Returns Boolean. See Q1099 for contains (substring), Q1102 for is in with ranges.

defines module qa.operators.isinlist

  defines program

    IsInListDemo()
      stdout <- Stdout()

      fruits <- List() of String
      fruits += "apple"
      fruits += "banana"
      fruits += "cherry"

      //Check membership
      found <- "banana" is in fruits
      stdout.println(`banana in list: ${found}`)

      //Check absence
      missing <- "grape" is in fruits
      stdout.println(`grape in list: ${missing}`)

Python: item in list. Java: list.contains(item). Rust: list.contains(&item). Go: manual loop. EK9: item is in list — keyword expression.

check
collection
contains
is in
list
membership


Question: Check if a score falls within the range 1 to 10.

I need to validate that a number is between a minimum and maximum

In Python I'd use 'if 1 <= x <= 10'. Write the EK9 range check

Given a user input, verify it falls within an allowed range using 'is in'

Test whether an integer is within a start ... end range

Answer:
Range membership:
  valid <- score is in 1 ... 10

Inclusive on both ends. Works with any comparable type (Integer, Float, Character, Date). Bounds can be variables. See Q1101 for is in with collections, Q1099 for contains (substring).

defines module qa.operators.isinrange

  defines program

    IsInRangeDemo()
      stdout <- Stdout()

      upperBound <- 10

      //Inside the range
      inside <- 5 is in 1 ... upperBound
      stdout.println(`5 in 1..10: ${inside}`)

      //Outside the range
      outside <- 15 is in 1 ... upperBound
      stdout.println(`15 in 1..10: ${outside}`)

      //On the boundary (inclusive)
      boundary <- 1 is in 1 ... upperBound
      stdout.println(`1 in 1..10: ${boundary}`)

between
bounds
inclusive
is in
range
validate


Question: Increment an Integer, a Float, and a Character value.

I need to advance a counter, increase a float, and step a character to the next letter

In Java I'd use ++ on an int. Show EK9 increment on different types

Given an Integer, Float, and Character, apply ++ and -- to each

Use the increment and decrement operators across multiple EK9 types

Answer:
Increment and decrement:
  count++
  letter++

Statement-only operators -- cannot be used in expressions. Works on Integer, Float, Character, and other ordered types. See Q967 for increment/decrement rules, Q780 for statement-only restriction.

defines module qa.operators.incrementoperators

  defines program

    IncrementOperatorsDemo()
      stdout <- Stdout()

      //Integer increment
      count <- 10
      count++
      stdout.println(`After ++: ${count}`)
      count--
      stdout.println(`After --: ${count}`)

      //Float increment
      price <- 9.99
      price++
      stdout.println(`Float after ++: ${price}`)

      //Character increment — advances to next code point
      letter <- 'a'
      letter++
      stdout.println(`'a' after ++: ${letter}`)

      //Multiple increments
      digit <- 'A'
      digit++
      digit++
      digit++
      stdout.println(`'A' after +++: ${digit}`)

Java: count++ (expression). C: count++ (expression). EK9: count++ (statement ONLY — cannot use in expressions like x = count++).

++ -- Character Float Integer decrement increment

++ increment
-- decrement
statement operators

Explanation: ++ and -- are statement-only operators in EK9. They cannot be used in expressions or assignments.


Question: Get the absolute value of -42 and the square root of 16.

I need to remove the sign from a negative number and compute a square root

In Java I'd use Math.abs() and Math.sqrt(). Write the EK9 equivalents

Given a negative integer and a float, apply abs and sqrt prefix operators

Compute absolute value and square root using EK9's unary prefix operators

Answer:
Prefix unary operators:
  positive <- abs negVal
  side <- sqrt area

Keyword operators, not method calls -- no parentheses needed. abs works on Integer and Float; sqrt on Float. See Q240 for arithmetic operators.

defines module qa.operators.unaryabssqrt

  defines program

    UnaryAbsSqrtDemo()
      stdout <- Stdout()

      //Absolute value of a negative integer
      negVal <- -42
      positive <- abs negVal
      stdout.println(`abs -42: ${positive}`)

      //Square root of a float
      area <- 16.0
      side <- sqrt area
      stdout.println(`sqrt 16.0: ${side}`)

      //Absolute value of a negative float
      temperature <- -5.7
      magnitude <- abs temperature
      stdout.println(`abs -5.7: ${magnitude}`)

Java: Math.abs(x), Math.sqrt(x). Python: abs(x), math.sqrt(x). Rust: x.abs(), x.sqrt(). EK9: abs x, sqrt x — prefix operators, not functions.

abs
absolute
math
prefix
sqrt
square root
unary

abs operator
sqrt operator
prefix operators

Explanation: EK9 has no Math class; `Math.abs(negVal)` leaves 'Math' unresolved and triggers E50001 - use the 'abs' prefix operator: abs negVal. See ek9 -h E50001 for details.


Question: Create a Logger component with a name field and a log method.

I need to define a reusable service component with fields and methods

In Spring I'd use @Component on a class. Write the EK9 equivalent

Given a logging service, define it as a component with state and behaviour

Build a basic component that holds configuration and provides a method

Answer:
Define a component with fields and methods:

  defines component
    Logger
      name <- "DefaultLogger"
      log()
        -> message as String
        <- output as String: name + ": " + message

Components are DI-managed types with fields, constructors, methods, and operators.

See Q227 for compile-time DI validation. See Q1110 for register and inject.

defines module qa.di.definecomponent

  defines component

    Logger
      name <- "AppLogger"

      getName()
        <- rtn as String: name

      log()
        -> message as String
        <- output as String: `${name}: ${message}`

      default operator

  defines program

    DefineComponentDemo()
      stdout <- Stdout()

      logger <- Logger()
      result <- logger.log("Application started")
      stdout.println(result)
      stdout.println(`Logger name: ${logger.getName()}`)

Spring: @Component class. Guice: bind(Logger.class). .NET: services.AddSingleton<Logger>(). EK9: defines component + register in application.

DI component define fields methods service

Explanation: EK9 has no annotations. Use 'defines component' to declare DI-managed types.

Oracle can generate a component with fields and methods for dependency injection.


Question: Copy a component and check if the copy equals the original.

I need to duplicate a component and verify the copy matches

In Java I'd clone a service bean. Write the EK9 component copy

Given a Logger component, create a copy using :=: and test equality with ==

Duplicate a component's state into a new instance

Answer:
Copy and compare components using :=: and ==:

  copy <- Logger()
  copy :=: original
  if copy == original
    stdout.println("Match")

'default operator' generates :=: and == from component fields, same as for classes.

See Q1105 for defining components. See Q1075 for :=: copy details.

defines module qa.di.componentcopyequality

  defines component

    Config
      host <- "localhost"
      port <- 8080

      getHost()
        <- rtn as String: host

      getPort()
        <- rtn as Integer: port

      default operator

  defines program

    ComponentCopyEqualityDemo()
      stdout <- Stdout()

      original <- Config()
      stdout.println(`Original: ${original}`)

      //Copy
      backup <- Config()
      backup :=: original
      stdout.println(`Copy: ${backup}`)

      //Equality
      if original == backup
        stdout.println("Original and copy are equal")

Java: no standard bean cloning. Spring: prototype scope creates new instances. EK9: :=: copies component field values directly.

:=: == component copy duplicate equality

Explanation: EK9 has no .clone() method; use ':=:' to copy a component's fields. See ek9 -h E50060 for details.


Question: Check the set and unset states of a component's fields.

I need to verify which fields in a component have been initialised

In Java I'd check for null fields on a bean. Show the EK9 component isSet pattern

Given a component with default values, confirm operator ? reflects field state

Test whether a component is set when its fields have initial values

Answer:
Check component set status with ?:

  settings <- Settings()
  if settings?
    stdout.println("Settings ready")

Field initialisers (e.g., name <- "default") make the component SET immediately after construction.

See Q1087 for default operator ? semantics. See Q1105 for defining components.

defines module qa.di.componentissetstates

  defines component

    Settings
      theme <- "dark"
      fontSize <- 14

      getTheme()
        <- rtn as String: theme

      default operator

  defines program

    ComponentIsSetStatesDemo()
      stdout <- Stdout()

      //Component with field initialisers — set immediately
      settings <- Settings()
      stdout.println(`Settings set?: ${settings?}`)
      stdout.println(`Settings: ${settings}`)

Java: manual null checks on bean fields. Python: hasattr checks. EK9: operator ? on the component — any-field semantics by default.

? component fields initialised isSet state

Explanation: EK9 has no null; use the ? operator to test whether a component has meaningful data instead of comparing to null. See ek9 -h E01073 for details.


Question: Extend a BaseService component and add a version field to the child.

I have a base component and need to create a specialised version with extra fields

In Spring I'd extend a @Service class. Write the EK9 component inheritance

Given a BaseService, create an ExtendedService that adds version tracking

Build a component hierarchy with parent and child fields

Answer:
Mark parent 'as open' and extend with child:

  BaseService as open
    serviceName <- "Base"
    default operator

  ExtendedService extends BaseService
    version <- 1
    default operator

Child inherits parent fields. 'default operator' covers both parent and child fields.

See Q1105 for basic components. See Q1080 for class inheritance with :=: copy.

defines module qa.di.componentinheritance

  defines component

    BaseService as open
      serviceName <- "BaseService"

      default operator

  defines component

    ExtendedService extends BaseService
      version <- 1

      default operator

  defines program

    ComponentInheritanceDemo()
      stdout <- Stdout()

      base <- BaseService()
      stdout.println(`Base: ${base}`)

      ext <- ExtendedService()
      stdout.println(`Extended: ${ext}`)
      stdout.println(`Extended set?: ${ext?}`)

Spring: extend a @Component class. Guice: bind subclass to parent interface. EK9: component extends with 'as open' on parent.

child component extends inheritance open parent

Explanation: Components are closed by default. Add 'as open' to allow extension.


Question: Mutate a Counter component's state through increment and reset methods.

I need a component that tracks a count and provides methods to change it

In Java I'd have a mutable bean with setter methods. Write the EK9 equivalent

Given a counter component, call methods that modify its internal state

Build a stateful component and demonstrate state changes through method calls

Answer:
Non-pure methods modify component state:

  increment()
    count++
  reset()
    count: 0

Pure methods (readers) use 'as pure'; non-pure methods (writers) do not. The compiler enforces this.

See Q1105 for basic components. See Q1106 for copying component state.

defines module qa.di.componentmutation

  defines component

    Counter
      count <- 0

      increment()
        count++

      reset()
        count: 0

      getCount() as pure
        <- rtn as Integer: count

      default operator

  defines program

    ComponentMutationDemo()
      stdout <- Stdout()

      counter <- Counter()
      stdout.println(`Initial: ${counter.getCount()}`)

      counter.increment()
      counter.increment()
      counter.increment()
      stdout.println(`After 3 increments: ${counter.getCount()}`)

      counter.reset()
      stdout.println(`After reset: ${counter.getCount()}`)

Java: mutable service with fields and methods. Spring: @Service with state. EK9: component with mutable fields and non-pure methods.

component increment method mutable mutation state

component mutation
mutable component state

      increment() as pure
        count++

Explanation: Methods that modify state cannot be marked 'as pure'. Remove 'as pure' from mutating methods.


Question: Register a Logger service and inject it into a program.

I need to wire a component into a program using EK9's DI system

In Spring I'd use @Autowired to inject a service. Write the EK9 equivalent

Given an abstract Logger and concrete ConsoleLogger, register and inject it

Set up an application that provides a Logger to a program via injection

Answer:
Register concrete as abstract, inject with ! suffix:

  register ConsoleLogger() as Logger
  ...
  service as Logger!

The ! suffix marks an injection point. The compiler validates a matching registration exists.

See Q227 for compile-time DI validation. See Q228 for registration ordering.

defines module qa.di.registerinjectbasic

  defines component

    Logger as abstract
      log() as abstract
        -> message as String
        <- output as String?

      default operator ?

    ConsoleLogger is Logger
      override log()
        -> message as String
        <- output as String: `LOG: ${message}`

      default operator ?

  defines application

    BasicApp
      register ConsoleLogger() as Logger

  defines program

    RegisterInjectBasicDemo() with application of BasicApp
      stdout <- Stdout()

      //Injected via '!' suffix — compiler verified
      logger as Logger!
      result <- logger.log("Application started")
      stdout.println(result)

Spring: @Autowired Logger logger. Guice: @Inject Logger logger. .NET: services.AddSingleton<ILogger, ConsoleLogger>(). EK9: register + '!' suffix injection.

! DI application component inject register wire

Explanation: Every injection point (!) must have a matching registration in the application. The compiler rejects missing registrations.

Oracle can generate an application with component registration and program injection wiring.


Question: Register dependencies in the correct order so a service can inject them.

I need to ensure my Logger is registered before the MessageService that depends on it

In Spring ordering is automatic. Show the EK9 explicit registration ordering

Given three components with dependencies, register them in dependency order

Wire components so that dependencies are available before dependents are created

Answer:
Register dependencies before the components that inject them:

  register ConsoleLogger() as Logger
  register UpperFormatter() as Formatter
  register SimpleService() as Service

The compiler validates ordering: Logger must be registered before any component that injects Logger!.

See Q228 for ordering rules. See Q229 for circular dependency detection.

defines module qa.di.injectorder

  defines component

    Logger as abstract
      log() as abstract
        -> message as String
        <- output as String?
      default operator ?

    ConsoleLogger is Logger
      override log()
        -> message as String
        <- output as String: `LOG: ${message}`
      default operator ?

    Formatter as abstract
      format() as abstract
        -> text as String
        <- result as String?
      default operator ?

    UpperFormatter is Formatter
      override format()
        -> text as String
        <- result as String: text.upperCase()
      default operator ?

    Service as abstract
      process() as abstract
        -> input as String
        <- output as String?
      default operator ?

    SimpleService is Service
      logger as Logger!
      formatter as Formatter!

      override process()
        -> input as String
        <- output <- String()
        formatted <- formatter.format(input)
        output: logger.log(formatted)

      default operator ?

  defines application

    OrderedApp
      //Dependencies first, dependents after
      register ConsoleLogger() as Logger
      register UpperFormatter() as Formatter
      register SimpleService() as Service

  defines program

    InjectOrderingDemo() with application of OrderedApp
      stdout <- Stdout()
      service as Service!
      result <- service.process("hello world")
      stdout.println(result)

Spring: automatic ordering via @DependsOn or constructor analysis. Guice: Module.configure() ordering matters. EK9: explicit top-to-bottom ordering validated at compile time.

DI before compile-time dependency ordering register

      register SimpleService() as Service
      register ConsoleLogger() as Logger
      register UpperFormatter() as Formatter

Explanation: Dependencies must be registered BEFORE the components that inject them; registering SimpleService (which injects Logger and Formatter) before them triggers E08200. See ek9 -h E08200 for details.


Question: Inject a Logger dependency into a component's field using the ! suffix.

I need a MessageService component that gets its Logger from the DI container

In Spring I'd use @Autowired on a field. Show the EK9 field injection pattern

Given a service component, declare an injection field with the ! suffix

Wire a Logger into a MessageService component through field injection

Answer:
Declare injection fields with the ! suffix:

  MessageService is Service
    logger as Logger!

The compiler validates at compile time that a matching registration exists. Only abstract component types can be injection targets.

See Q1110 for register/inject basics. See Q227 for compile-time validation.

defines module qa.di.fieldinjection

  defines component

    Logger as abstract
      log() as abstract
        -> message as String
        <- output as String?
      default operator ?

    ConsoleLogger is Logger
      override log()
        -> message as String
        <- output as String: `[INFO] ${message}`
      default operator ?

    Service as abstract
      send() as abstract
        -> message as String
        <- result as String?
      default operator ?

    //logger field is injected via ! suffix
    MessageService is Service
      logger as Logger!

      override send()
        -> message as String
        <- result <- String()
        result: logger.log(message)

      default operator ?

  defines application

    FieldInjectionApp
      register ConsoleLogger() as Logger
      register MessageService() as Service

  defines program

    FieldInjectionDemo() with application of FieldInjectionApp
      stdout <- Stdout()
      service as Service!
      result <- service.send("Hello from DI")
      stdout.println(result)

Spring: @Autowired private Logger logger. Guice: @Inject Logger logger. EK9: logger as Logger! — the ! suffix marks injection.

! autowired component dependency field inject

Explanation: Injection fields must use abstract types. Inject the abstraction (Logger), not the concrete implementation (ConsoleLogger).


Question: Define an application that wires three components together.

I need an application block that registers Logger, Formatter, and Service

In Spring I'd use @Configuration with @Bean methods. Write the EK9 application

Given three components with dependencies, define the application wiring

Set up a complete application registry with multiple registrations

Answer:
Create the DI registry and connect it to a program:

  defines application
    MyApp
      register ConsoleLogger() as Logger
      register GreetingService() as Service

  defines program
    Main() with application of MyApp

'with application of' connects the program to the registry. All injection points resolve from this application.

See Q1110 for register/inject basics. See Q1111 for ordering.

defines module qa.di.applicationregistry

  defines component

    Logger as abstract
      log() as abstract
        -> message as String
        <- output as String?
      default operator ?

    ConsoleLogger is Logger
      override log()
        -> message as String
        <- output as String: `[LOG] ${message}`
      default operator ?

    Formatter as abstract
      format() as abstract
        -> text as String
        <- result as String?
      default operator ?

    UpperFormatter is Formatter
      override format()
        -> text as String
        <- result as String: text.upperCase()
      default operator ?

    Service as abstract
      greet() as abstract
        -> name as String
        <- greeting as String?
      default operator ?

    GreetingService is Service
      logger as Logger!
      formatter as Formatter!

      override greet()
        -> name as String
        <- greeting <- String()
        formatted <- formatter.format(name)
        greeting: logger.log(formatted)

      default operator ?

  defines application

    GreetApp
      register ConsoleLogger() as Logger
      register UpperFormatter() as Formatter
      register GreetingService() as Service

  defines program

    ApplicationRegistryDemo() with application of GreetApp
      stdout <- Stdout()
      service as Service!
      result <- service.greet("World")
      stdout.println(result)

Spring: @SpringBootApplication + @Configuration. Guice: AbstractModule.configure(). .NET: Program.cs with builder.Services. EK9: defines application with register statements.

DI application configuration register registry wire

defines application
application registry

Explanation: A program that uses DI injection must be linked to its application with 'with application of AppName' (EK9 has no wiring annotations). See ek9 -h E08220 for details.

Oracle can generate an application that wires multiple components together.