Question: Define a sealed Shape class that permits only Circle, Square, and Triangle.

Restrict which classes can extend Shape using 'allow only'

I need a closed hierarchy where only specific subclasses are allowed

In Kotlin I'd use a sealed class. Write the EK9 sealed class equivalent

Given a Shape base, lock it down so no other subclass can be added

Answer:
Seal with 'allow only' and list permitted subtypes:
  Shape allow only Circle, Square, Triangle as open
    area()
      <- rtn as String: "Shape"

  Circle extends Shape
    override area()
      <- rtn as String: "Circle"

Only the listed types can extend Shape. The compiler rejects any other subclass.

See Q298 for sealed traits. See Q1121 for dispatching on sealed types.

defines module qa.sealedtraits.sealedclassbasic

  defines class

    Shape allow only Circle, Square, Triangle as open
      area()
        <- rtn as String: "Shape"

    Circle extends Shape
      override area()
        <- rtn as String: "Circle"

    Square extends Shape
      override area()
        <- rtn as String: "Square"

    Triangle extends Shape
      override area()
        <- rtn as String: "Triangle"

  defines program

    SealedClassBasicDemo()
      stdout <- Stdout()

      circle <- Circle()
      square <- Square()
      triangle <- Triangle()

      stdout.println(circle.area())
      stdout.println(square.area())
      stdout.println(triangle.area())

Kotlin: sealed class Shape. Java 17: sealed class Shape permits Circle, Square. Rust: enum Shape { Circle, Square }. EK9: Shape allow only Circle, Square as open.

allow only
class
closed
hierarchy
restricted
sealed

Explanation: A sealed class listing 'allow only Circle, Square' forbids Triangle from extending it — every permitted subtype must appear in the allow-only list. See ek9 -h E05240 for details.


Question: Build a chained sealed hierarchy: Vehicle permits Car and Truck, Car permits Sedan and Hatchback.

Create a multi-level sealed class tree with transitive allow only

I need Vehicle sealed to Car/Truck, and Car itself sealed to Sedan/Hatchback

In Java 17 I'd chain sealed permits. Write the EK9 multi-level sealed hierarchy

Given a two-level hierarchy, seal both levels with their own allow only lists

Answer:
Chain 'allow only' at each level:
  Vehicle allow only Car, Truck, Sedan, Hatchback as open
  Car extends Vehicle allow only Sedan, Hatchback as open
  Sedan extends Car
  Truck extends Vehicle

Each sealed class lists ALL permitted descendants in its own 'allow only'. The compiler enforces at both levels.

See Q1171 for basic sealed class. See Q303 for chained sealing.

defines module qa.sealedtraits.sealedclasshierarchy

  defines class

    Vehicle allow only Car, Truck, Sedan, Hatchback as open
      describe()
        <- rtn as String: "Vehicle"

    Car extends Vehicle allow only Sedan, Hatchback as open
      override describe()
        <- rtn as String: "Car"

    Sedan extends Car
      override describe()
        <- rtn as String: "Sedan"

    Hatchback extends Car
      override describe()
        <- rtn as String: "Hatchback"

    Truck extends Vehicle
      override describe()
        <- rtn as String: "Truck"

  defines program

    SealedClassHierarchyDemo()
      stdout <- Stdout()

      sedan <- Sedan()
      hatchback <- Hatchback()
      truck <- Truck()

      stdout.println(sedan.describe())
      stdout.println(hatchback.describe())
      stdout.println(truck.describe())

Java 17: sealed class Vehicle permits Car, Truck; sealed class Car extends Vehicle permits Sedan. Kotlin: nested sealed classes. EK9: chained 'allow only' at each level.

allow only
chained
hierarchy
multi-level
sealed
transitive

chained sealed classes
multi-level sealed hierarchy

Explanation: The top-level sealed class must list ALL descendants, including indirect ones (Sedan, Hatchback) — not just direct children.


Question: Increment a date and calculate whether two dates differ.

I need to advance a date by one day using the ++ operator

In Java I'd use LocalDate.plusDays(1). Write the EK9 date increment

Given a date, step forward one day and compare with another date

Move a date forward using ++ and check equality with ==

Answer:
++ advances Date by one day, <=> compares:
  today++
  cmp <- today <=> targetDate

-- goes backward. DateTime increments by one second instead. See Q1103.

defines module qa.datetimeduration.datearithmetic

  defines program

    DateArithmeticDemo()
      stdout <- Stdout()

      today <- 2026-04-07
      stdout.println(`Today: ${today}`)

      //Increment by one day
      today++
      stdout.println(`Tomorrow: ${today}`)

      //Compare dates
      targetDate <- 2026-04-09
      cmp <- today <=> targetDate
      stdout.println(`Comparison: ${cmp}`)

Java: date.plusDays(1). Python: date + timedelta(days=1). Rust: date + Duration::days(1). EK9: date++ (one day forward).

++ arithmetic compare date day increment

Explanation: EK9 Date has no plusDays() method — use ++ to advance a Date by one day. See ek9 -h E50060 for details.


Question: Calculate the duration between two dates.

Write code to find the time span between a start and end date

I have two dates and need the duration between them

Given a start date and end date, compute how much time has elapsed

In Java I'd use ChronoUnit.DAYS.between(). Write the EK9 equivalent

Answer:
Subtract one date from another using the - operator:
  dur <- endDate - startDate
  stdout.println(dur)

Date subtraction produces a Duration. Use component accessors like .days(), .hours() to extract parts. Negative durations occur when subtracting a later date from an earlier one. See Q539 for date difference details. See Q540 for adding time.

defines module qa.datetime.durationbetween

  defines program

    DurationBetweenDemo()
      stdout <- Stdout()

      // Subtract two dates to get a Duration
      startDate <- 2024-01-01
      endDate <- 2024-12-31
      dur <- endDate - startDate
      stdout.println(`Duration: ${dur}`)
      stdout.println(`Days: ${dur.days()}`)

      // Time subtraction
      startTime <- 09:00
      endTime <- 17:30
      workDay <- endTime - startTime
      stdout.println(`Work day: ${workDay}`)
      stdout.println(`Hours: ${workDay.hours()}`)

      // Duration literal comparison
      oneWeek <- P7D
      twoWeeks <- P14D
      stdout.println(`One week < two weeks: ${oneWeek < twoWeeks}`)

      // Negative duration
      backwards <- startDate - endDate
      stdout.println(`Backwards: ${backwards}`)

Java: ChronoUnit.DAYS.between(d1, d2) or Period.between(). Python: (date2 - date1).days. Rust: chrono signed_duration_since(). Go: t2.Sub(t1). EK9: endDate - startDate gives Duration directly.

between
date
days
difference
duration
elapsed
subtract
time span

duration between dates
date subtraction
time span

Explanation: Date has no daysBetween() method. Use the - operator to subtract dates and get a Duration, then call .days() for the day count. See ek9 -h E50060 for details.


Question: Define a Product record with name, price, and a constructor.

Write code to create a simple data record in EK9

I need a lightweight data type with public fields for name and price

Given name and price fields, define a record type with default operators

In Java I'd use record Product(String name, double price). Write the EK9 equivalent

Answer:
Define a record with public fields, a constructor, and default operator:
  defines record
    Product
      name as String: String()
      price as Float: 0.0
      Product()
        -> ...
      default operator

Record fields are always public (accessed directly with product.name). Records cannot have methods — only constructors and operators. Use 'default operator' to auto-generate standard operators. See Q97 for class vs record. See Q1060 for record field visibility.

defines module qa.classesandoop.definerecord

  defines record

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

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

      default operator

  defines program

    DefineRecordDemo()
      stdout <- Stdout()

      // Create a product using the constructor
      widget <- Product("Widget", 19.99)

      // Access fields directly (public)
      stdout.println(`Name: ${widget.name}`)
      stdout.println(`Price: ${widget.price}`)

      // Default operators provide $, ==, <>, <=> etc.
      stdout.println(`Product: ${widget}`)

      // Create another and compare
      gadget <- Product("Gadget", 29.99)
      stdout.println(`Equal: ${widget == gadget}`)

      // Records in a list
      products <- [widget, gadget, Product("Gizmo", 9.99)]
      for product in products
        stdout.println(`${product.name}: \$${product.price}`)

Java: record Product(String name, double price) — immutable, auto accessors. Python: @dataclass class Product. Rust: struct Product { name: String, price: f64 }. Kotlin: data class Product(val name: String, val price: Double). EK9: defines record with public fields, mutable by default, 'as pure' controls mutability.

constructor
data
default operator
define
fields
product
public
record

define record
record constructor
data type

getName()
        <- rtn as String: name

Explanation: Records can only have constructors and operators, not methods. Adding a method like getName() triggers E07290. Access record fields directly since they are public. See ek9 -h E07290 for details.

Explanation: Record fields are public and accessed directly without parentheses. Using product.name() tries to call a method, but records do not have accessor methods. See ek9 -h E06180 for details.


Question: Implement a Describable trait with a name method in a concrete Animal class.

Write code to define a trait and implement it in a class with a constructor

I need a class that fulfils a Describable contract with name and describe methods

In Java I'd implement an interface on a class. Write the EK9 trait implementation

Given a Describable trait with describe(), create a Dog class that provides the implementation

Answer:
Implement a trait with 'with trait of' and override abstract methods:

  Dog with trait of Describable
    override describe()
      <- rtn as String: `Dog: ${name}`

Use 'override' on every abstract trait method. The trait can have a default method that the class inherits without overriding.

See Q106 for trait basics. See Q108 for implementing traits. See Q1129 for trait delegation.

defines module qa.sealedtraits.traitwithclass

  defines trait

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

      summary() as pure
        <- rtn as String: "No summary available"

  defines class

    Dog with trait of Describable
      name <- String()

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

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

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

      default operator

  defines program

    TraitWithClassDemo()
      stdout <- Stdout()

      dog <- Dog("Rex")
      stdout.println(dog.describe())
      stdout.println(dog.summary())
      stdout.println(`IsSet: ${dog?}`)

Java: class Dog implements Describable. C#: class Dog : IDescribable. Python: class Dog(Describable). Kotlin: class Dog : Describable. EK9: Dog with trait of Describable, override methods.

abstract
class
describe
implement
override
trait
with trait of

Explanation: When implementing a trait's abstract method, the 'override' keyword is mandatory. Omitting it triggers E05120.

Oracle can generate a class implementing a trait with all required method stubs.


Question: Use fully qualified enum values in a switch to map Direction to its opposite.

Write code to switch on an enum and produce the reverse direction

I need a function that returns the opposite of a compass Direction enum value

In Java I'd use switch(dir) { case NORTH: ... }. Write the EK9 qualified enum switch

Given North/South/East/West, create a function that maps each to its opposite using switch

Answer:
Qualify enum values with the type name in switch cases:
  switch dir
    case Direction.North
      rtn: Direction.South
    case Direction.South
      rtn: Direction.North

Always use Direction.North, never bare North. EK9 requires full qualification to prevent ambiguity.

See Q1170 for enum switch basics. See Q219 for enum features.

defines module qa.enumerations.enumqualifiedvalues

  defines type

    Direction
      North
      South
      East
      West

  defines function

    opposite()
      -> dir as Direction
      <- rtn as Direction: Direction.North

      switch dir
        case Direction.North
          rtn: Direction.South
        case Direction.South
          rtn: Direction.North
        case Direction.East
          rtn: Direction.West
        case Direction.West
          rtn: Direction.East
        default
          rtn: Direction.North

  defines program

    EnumQualifiedValuesDemo()
      stdout <- Stdout()

      directions <- [Direction.North, Direction.South, Direction.East, Direction.West]

      for dir in directions
        result <- opposite(dir)
        stdout.println(`${dir} -> ${result}`)

Java: case NORTH: (unqualified in switch). Kotlin: Direction.NORTH (qualified). Python: Direction.NORTH. EK9: Direction.North (always qualified).

Direction case enum opposite qualified switch

Explanation: EK9 enums must be qualified with the type name. Use Direction.North, not just North.


Question: Create a Dict, add entries with DictEntry, and look up values with getOrDefault.

Write code to build a dictionary and retrieve values safely

I need a key-value map with add, lookup, and size operations in EK9

In Python I'd use dict[key] = value and dict.get(key, default). Write the EK9 Dict equivalent

Given a mapping of product names to prices, show Dict creation, adding, and lookup

Answer:
Use Dict() of (K, V) with DictEntry and getOrDefault:

  prices <- Dict() of (String, Float)
  prices += DictEntry("Widget", 9.99)
  result <- prices.getOrDefault("Widget", 0.0)

Dict uses parenthesised type parameters. Add entries with DictEntry(key, value). Retrieve with getOrDefault(key, fallback) — there is no .get() method.

See Q1161 for comprehensive Dict. See Q1158 for Dict iteration. See Q129 for missing keys.

defines module qa.collections.dictcorrectsyntax

  defines program

    DictCorrectSyntaxDemo()
      stdout <- Stdout()

      //Create a Dict with parenthesised type parameters
      prices <- Dict() of (String, Float)

      //Add entries using DictEntry(key, value)
      prices += DictEntry("Widget", 9.99)
      prices += DictEntry("Gadget", 24.50)
      prices += DictEntry("Gizmo", 4.99)

      //Look up existing key — returns the value
      widgetPrice <- prices.getOrDefault("Widget", 0.0)
      stdout.println(`Widget: ${widgetPrice}`)

      //Look up missing key — returns the default
      missingPrice <- prices.getOrDefault("Unknown", 0.0)
      stdout.println(`Unknown: ${missingPrice}`)

      //Check size
      stdout.println(`Size: ${length prices}`)

      //Iterate over all entries
      for entry in prices
        stdout.println($entry)

      //Check if a key exists via getOrDefault
      fallback <- 0.0
      found <- prices.getOrDefault("Gadget", fallback)
      if found <> fallback
        stdout.println(`Gadget found: ${found}`)

Python: dict[k] = v, dict.get(k, default). Java: map.put(k, v), map.getOrDefault(k, default). Go: m[k] = v, if val, ok := m[k]. EK9: += DictEntry(k, v), getOrDefault(k, fallback).

Dict DictEntry add getOrDefault key-value lookup map

Explanation: Dict has no get() method, so calling prices.get("Widget") is unresolved — use getOrDefault(key, defaultValue) instead. See ek9 -h E50060 for details.


Question: Add entries to a Dict using DictEntry -- there is no curly-brace literal syntax.

Write code showing that EK9 dicts cannot use {key: value} literals

I keep getting errors trying to initialise a dict with curly braces in EK9

In JavaScript I'd use {name: 'Alice', age: 30}. What is the EK9 Dict equivalent?

Build a settings Dict the correct way using DictEntry instead of literal syntax

Answer:
EK9 has NO dict literal syntax. Build dicts with DictEntry:

  settings <- Dict() of (String, String)
  settings += DictEntry("theme", "dark")
  settings += DictEntry("lang", "en")

Do NOT try {"theme": "dark"} — it will not parse. Every entry uses DictEntry(key, value) added with +=.

See Q1178 for Dict creation. See Q1161 for full Dict operations.

defines module qa.collections.dictnoliteralsyntax

  defines program

    DictNoLiteralSyntaxDemo()
      stdout <- Stdout()

      //WRONG: EK9 has no curly-brace dict literals
      //settings <- {"theme": "dark", "lang": "en"}  <- PARSE ERROR

      //RIGHT: Create Dict and add entries with DictEntry
      settings <- Dict() of (String, String)
      settings += DictEntry("theme", "dark")
      settings += DictEntry("lang", "en")
      settings += DictEntry("fontSize", "14")

      //Retrieve values
      theme <- settings.getOrDefault("theme", "light")
      stdout.println(`Theme: ${theme}`)

      lang <- settings.getOrDefault("lang", "en")
      stdout.println(`Language: ${lang}`)

      //Iterate
      for entry in settings
        stdout.println($entry)

JavaScript: {key: value}. Python: {key: value}. Java: Map.of(k, v). Go: map[string]string{k: v}. EK9: NO literal syntax — use Dict() of (K, V) and += DictEntry(k, v).

Dict
DictEntry
curly brace
literal
no literal
syntax

Explanation: EK9 has no curly-brace dict literal. Use Dict() of (K, V) and add entries with += DictEntry(k, v).


Question: Define an abstract Shape class with an area method, and a concrete Circle subclass.

Write code for an abstract class hierarchy with Shape and Circle

I need an abstract Shape with area() and a concrete Circle that provides the implementation

In Java I'd use abstract class Shape with abstract double area(). Write the EK9 version

Create a Shape/Circle hierarchy showing abstract methods, override, and default operator placement

Answer:
Abstract classes use 'as abstract' and concrete children override:

  Shape as abstract
    area() as abstract
      <- rtn as Float?
    default operator ?

  Circle extends Shape
    override area()
      <- rtn as Float: ...
    override operator ? as pure
      ...
    default operator

Do NOT use 'default operator' on the abstract parent -- use only 'default operator ?' because abstract types cannot generate all operators. Concrete children use 'default operator' LAST.

See Q103 for abstract classes. See Q116 for default operator.

defines module qa.classesandoop.abstractclassoperators

  defines class

    Shape as abstract
      area() as abstract
        <- rtn as Float?

      default operator ?

    Circle extends Shape
      radius <- 0.0

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

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

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

      operator $ as pure
        <- rtn as String: `Circle(radius=${radius})`

  defines program

    AbstractClassOperatorsDemo()
      stdout <- Stdout()

      circle <- Circle(5.0)
      stdout.println(`Area: ${circle.area()}`)
      stdout.println(`IsSet: ${circle?}`)

      //Polymorphism with abstract type
      shapes <- List() of Shape
      shapes += Circle(3.0)
      shapes += Circle(7.5)

      for shape in shapes
        stdout.println(`Shape area: ${shape.area()}`)

Java: abstract class Shape { abstract double area(); }. Python: class Shape(ABC): @abstractmethod. Rust: trait Shape { fn area(&self) -> f64; }. EK9: Shape as abstract with area() as abstract.

abstract
area
circle
class
default operator
override
shape

Explanation: Concrete subclasses must implement all abstract methods. Omitting the override triggers E07130.


Question: Create a dynamic function that captures a greeting and prepends it to any input.

Write code for a closure that captures a local variable as state

I need a dynamic function extending an abstract function type with named capture

In JavaScript I'd use a closure over a variable. Write the EK9 dynamic function equivalent

Build a Greeter dynamic function that captures a greeting string and prepends it to input

Answer:
Define an abstract function type, then extend it dynamically:

  Greeter as abstract
    -> name as String
    <- message as String?

  greet <- (greeting: greetingText) extends Greeter as function
    message: `${greeting} ${name}`

Use 'as abstract' on the function definition. Dynamic functions use 'extends TypeName as function'. Named capture (greeting: greetingText) copies greetingText into field 'greeting'.

See Q1125 for dynamic function capture. See Q1126 for dynamic classes.

defines module qa.advancedtypes.dynamicfunctioncorrect

  defines function

    Greeter as abstract
      -> name as String
      <- message as String?

  defines program

    DynamicFunctionCorrectDemo()
      stdout <- Stdout()

      greetingText <- "Hello"

      //Create dynamic function with named capture
      greet <- (greeting: greetingText) extends Greeter as function
        message: `${greeting} ${name}`

      stdout.println(greet("Alice"))
      stdout.println(greet("Bob"))

      //Another dynamic function with different captured state
      farewellText <- "Goodbye"
      farewell <- (farewell: farewellText) extends Greeter as function
        message: `${farewell} ${name}`

      stdout.println(farewell("Charlie"))

JavaScript: const greet = (name) => greeting + name (closure). Java: Function<String,String> greet = name -> greeting + name. Python: lambda name: greeting + name. EK9: (field: source) extends AbstractType as function.

abstract capture closure dynamic extends function greeting

dynamic function syntax
abstract function


Question: Define a generic Pair class with two type parameters and proper field initialisation.

Write code for a generic Pair with typed fields and both required constructors

I need a generic container holding two values of different types with default operator

In Java I'd use Pair<A,B>. Write the EK9 generic Pair with field initialisation

Create a Pair of type (A, B) with declaration-assigns for fields and override operator ?

Answer:
Generic classes need two constructors and declaration-assigns for fields:

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

Fields use 'as T: T()' for initialisation. The default (no-arg) constructor is required for type inference. Override operator ? BEFORE default operator.

See Q1071 for two-constructor requirement. See Q194 for generic basics.

defines module qa.genericsdeep.genericclassfields

  defines class

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

      //Constructor 1: default (required for generic type inference)
      default Pair()

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

      first() as pure
        <- rtn as A: first

      second() as pure
        <- rtn as B: second

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

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

  defines program

    GenericClassFieldsDemo()
      stdout <- Stdout()

      nameAge <- Pair("Alice", 30)
      stdout.println(`First: ${nameAge.first()}`)
      stdout.println(`Second: ${nameAge.second()}`)
      stdout.println(`Pair: ${nameAge}`)

      coords <- Pair(10.5, 20.3)
      stdout.println(`Coords: ${coords}`)

Java: class Pair<A,B> { A first; B second; }. Kotlin: data class Pair<A,B>(val first: A, val second: B). Rust: struct Pair<A,B> { first: A, second: B }. EK9: Pair of type (A, B) with two constructors.

Pair
constructor
default
field
generic
two constructors
type parameter

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

Explanation: Generic types require exactly two constructors: a default (no-arg) and a parameterised one. Missing the default triggers E06040.


Question: Register a concrete Logger as an abstract Logger type for dependency injection.

Write code to wire a concrete component to its abstract interface for DI

I need to register ConsoleLogger as Logger and inject Logger into a program

In Spring I'd define @Component ConsoleLogger implements Logger. Write the EK9 DI equivalent

Set up application registration of a concrete type as its abstract parent for injection

Answer:
Register concrete as abstract, inject with the abstract type:

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

The '!' suffix marks an injection point. Injection fields MUST use the abstract type (Logger!), not the concrete type (ConsoleLogger!). The compiler validates a matching registration exists.

See Q1110 for DI basics. See Q227 for compile-time validation. See Q228 for ordering.

defines module qa.di.abstractrequired

  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 ?

  defines application

    LoggingApp
      register ConsoleLogger() as Logger

  defines program

    DiAbstractRequiredDemo() with application of LoggingApp
      stdout <- Stdout()

      //Inject with the ABSTRACT type, not the concrete type
      logger as Logger!

      result <- logger.log("Service started")
      stdout.println(result)

      result2 <- logger.log("Processing request")
      stdout.println(result2)

Spring: @Autowired Logger logger (interface). Guice: bind(Logger.class).to(ConsoleLogger.class). .NET: services.AddSingleton<ILogger, ConsoleLogger>(). EK9: register ConcreteType() as AbstractType, inject with AbstractType!.

DI abstract application component concrete inject register

Explanation: Every injection point (!) must have a matching registration. The compiler rejects missing registrations with E08210.


Question: Extract magic literals in comparisons into named constants to satisfy the compiler.

Write code that avoids magic numbers in comparisons by using named variables

I keep getting E11064 when comparing against literal values in EK9

In Java I'd use static final for magic numbers. What is the EK9 pattern for named constants?

Fix a comparison that uses a bare literal by extracting it into a named constant

Answer:
Name all literals used in comparisons:

  expensiveThreshold <- 50.0
  if price > expensiveThreshold
    stdout.println("Expensive")

EK9 rejects 'price > 50.0' with E11064 — magic literals in comparisons must be named. This improves readability and makes intent explicit.

See Q238 for operator set. See Q240 for arithmetic operators.

defines module qa.operators.namedconstantsrequired

  defines function

    isExpensive()
      -> price as Float
      <- rtn as Boolean: false

      //Named constant — compiler accepts this
      expensiveThreshold <- 50.0
      rtn: price > expensiveThreshold

    isLongString()
      -> item as String
      <- rtn as Boolean: false

      //Named constant for string length comparison
      minLength <- 5
      rtn: length item > minLength

  defines program

    NamedConstantsRequiredDemo()
      stdout <- Stdout()

      //Use named constants in comparisons
      price <- 75.0
      stdout.println(`Expensive: ${isExpensive(price)}`)

      cheapPrice <- 10.0
      stdout.println(`Expensive: ${isExpensive(cheapPrice)}`)

      longWord <- "International"
      stdout.println(`Long: ${isLongString(longWord)}`)

      shortWord <- "Hi"
      stdout.println(`Long: ${isLongString(shortWord)}`)

Java: private static final double THRESHOLD = 50.0. Python: THRESHOLD = 50.0 (convention). Go: const threshold = 50.0. EK9: threshold <- 50.0 (compiler-enforced naming).

E11064 comparison constant literal magic named threshold

named constants
magic literal elimination

Explanation: EK9 rejects magic literals in comparisons. Extract 50.0 into a named variable like expensiveThreshold.


Question: Define a Percentage type constrained to values between 0 and 100.

Write code to create a constrained numeric type that rejects out-of-range values

I need a type that only accepts integers from 0 to 100 for percentage values

In Rust I'd use a newtype with validation. Write the EK9 constrained type equivalent

Build a Percentage constrained type and show how invalid values become unset

Answer:
Use 'constrain as' with range checks:

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

A bare constructor ASSERTS validity: Percentage(150) Panics at runtime on an out-of-range value, and a violating literal constant is the compile error E08260. For untrusted input use the fallible factory Percentage().of(value), which returns an UNSET object on violation (never Panics) — check with '?' after construction. Constrained types are disconnected from their base type — Percentage is NOT an Integer subtype.

See Q257 for constrained types. See Q1074 for constrained in record. See Q720 for constrainable types.

defines module qa.advancedtypes.constrainedtype

  defines type

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

  defines record

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

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

      default operator

  defines program

    ConstrainedTypeDemo()
      stdout <- Stdout()

      //Valid — within range
      passing <- Percentage(85)
      stdout.println(`Valid score set: ${passing?}`)
      if passing?
        stdout.println(`Score: ${passing}`)

      //Invalid — above 100: the fallible factory returns unset (a bare Percentage(150) would Panic at
      //runtime, and a violating literal is the compile error E08260)
      tooHigh <- Percentage().of(150)
      stdout.println(`Too high set: ${tooHigh?}`)

      //Invalid — below 0: of() returns unset
      negative <- Percentage().of(-5)
      stdout.println(`Negative set: ${negative?}`)

      //Boundary values
      zero <- Percentage(0)
      hundred <- Percentage(100)
      stdout.println(`Zero set: ${zero?}`)
      stdout.println(`Hundred set: ${hundred?}`)

      //Use in a record
      result <- ExamScore("Alice", Percentage(92))
      stdout.println(`${result}`)

Java: no built-in, uses Bean Validation @Min/@Max. Python: no type-level constraints. Rust: newtype with manual validation. Kotlin: value class with init check. EK9: 'constrain as' creates a validated, disconnected type.

constrain as
constrained
disconnected
percentage
range
type
validation

Explanation: Constrained types are disconnected from their base type, so passing the Integer 92 where a Percentage is expected fails to resolve the ExamScore constructor — wrap it as Percentage(92). See ek9 -h E50060 for details.


Question: How do I compare two dates and determine which is earlier?

Compare two Date values and find which comes first in EK9.

I need to check whether a project start date is before the end date.

In Java I used isBefore() on LocalDate — what is the EK9 equivalent?

Answer:
EK9 Date values support all comparison operators directly. Use < > <= >= == <> for Boolean checks and <=> for Integer ordering.

DATE LITERALS
Write dates as YYYY-MM-DD:
  startDate <- 2024-01-15
  endDate <- 2024-06-30

COMPARISON
  if startDate < endDate          earlier date
  if startDate == endDate         same date
  ordering <- startDate <=> endDate  negative = earlier, zero = same, positive = later

COALESCING
  earlier <- startDate <? endDate   returns the earlier date
  later <- startDate >? endDate     returns the later date

No method calls like isBefore() or compareTo(). Operators work the same on Date as on Integer, String, or any other comparable type.

See Q542 for full temporal comparison. See Q238 for the complete operator set. See Q539 for date differences.

defines module qa.operators.comparedates

  defines function

    <?- Compare two dates and return spaceship ordering. -?>
    compareDates() as pure
      ->
        left as Date
        right as Date
      <- rtn as Integer: left <=> right

    <?- Check if the first date is before the second. -?>
    isBefore() as pure
      ->
        left as Date
        right as Date
      <- rtn as Boolean: left < right

    <?- Return the earlier of two dates using coalescing min. -?>
    earlierOf() as pure
      ->
        left as Date
        right as Date
      <- rtn as Date: left <? right

    <?- Return the later of two dates using coalescing max. -?>
    laterOf() as pure
      ->
        left as Date
        right as Date
      <- rtn as Date: left >? right

  defines program

    CompareDatesDemo()
      stdout <- Stdout()

      // === DATE LITERALS ===

      startDate <- 2024-01-15
      endDate <- 2024-06-30
      midYear <- 2024-06-30

      // === COMPARISON VIA FUNCTION ===

      before <- isBefore(startDate, endDate)
      stdout.println(`Start before end: ${before}`)

      // === SPACESHIP ORDERING VIA FUNCTION ===

      ordering <- compareDates(startDate, endDate)
      stdout.println(`Start <=> End: ${ordering}`)

      sameCheck <- compareDates(endDate, midYear)
      stdout.println(`End <=> MidYear: ${sameCheck}`)

      // === COALESCING: FIND EARLIER AND LATER ===

      earlier <- earlierOf(startDate, endDate)
      later <- laterOf(startDate, endDate)
      stdout.println(`Earlier: ${earlier}`)
      stdout.println(`Later: ${later}`)

      // === SORTED DATE LIST VIA STREAM ===

      dates <- List() of Date
      dates += endDate
      dates += startDate
      dates += midYear

      sorted <- cat dates | sort | collect as List of Date
      stdout.println(`Sorted dates: ${sorted}`)

      // === STRING CONVERSION ===

      dateStr <- $startDate
      stdout.println(`Start date as string: ${dateStr}`)

      // === ISSET CHECK ===

      require startDate?
      unsetDate <- Date()
      require ~unsetDate?

Java: LocalDate.isBefore(), compareTo(), isAfter() methods. Python: direct < > operators on datetime.date. Rust: PartialOrd on chrono::NaiveDate. Go: t.Before(), t.After() methods. Kotlin: compareTo() or operator overloading. EK9: direct < > <= >= == <> <=> operators on Date literals, no method calls needed.

after before coalescing compare date earlier later operator ordering spaceship temporal

date comparison
date operators
temporal ordering

Explanation: Date has no isBefore() method — EK9 uses comparison operators (left < right) directly on temporal types. See ek9 -h E50060 for details.


Question: How do I check if a meeting time is before noon in EK9?

Compare two Time values to see which is earlier.

I need to determine whether a scheduled time falls in the morning.

In Python I compared datetime.time objects — how does EK9 handle this?

Answer:
EK9 Time values use the same comparison operators as every other type. Write time literals as HH:MM or HH:MM:SS.

TIME LITERALS
  meetingTime <- 09:30
  noon <- 12:00
  precise <- 14:30:45

COMPARISON
  if meetingTime < noon          check if before noon
  ordering <- meetingTime <=> noon  negative = earlier, zero = same, positive = later

COALESCING
  earlierTime <- meetingTime <? noon  returns whichever is earlier

All comparison operators (< > <= >= == <> <=>) work identically on Time, Date, Integer, String, and any type that implements them.

See Q542 for full temporal comparison. See Q31 for Date and Time basics.

defines module qa.operators.comparetimes

  defines function

    <?- Check if a time is before another time. -?>
    isBeforeTime() as pure
      ->
        check as Time
        against as Time
      <- rtn as Boolean: check < against

    <?- Compare two times and return spaceship ordering. -?>
    compareTimes() as pure
      ->
        left as Time
        right as Time
      <- rtn as Integer: left <=> right

    <?- Return the earlier of two times using coalescing min. -?>
    earlierOf() as pure
      ->
        left as Time
        right as Time
      <- rtn as Time: left <? right

    <?- Return the later of two times using coalescing max. -?>
    laterOf() as pure
      ->
        left as Time
        right as Time
      <- rtn as Time: left >? right

  defines program

    CompareTimesDemo()
      stdout <- Stdout()

      // === TIME LITERALS ===

      meetingTime <- 09:30
      noon <- 12:00
      afternoon <- 14:30:45

      // === IS THE MEETING BEFORE NOON? ===

      isMorning <- isBeforeTime(meetingTime, noon)
      stdout.println(`Meeting is morning: ${isMorning}`)

      isAfternoonLater <- isBeforeTime(noon, afternoon)
      stdout.println(`Afternoon after noon: ${isAfternoonLater}`)

      // === SPACESHIP ORDERING VIA FUNCTION ===

      ordering <- compareTimes(meetingTime, noon)
      stdout.println(`Meeting <=> noon: ${ordering}`)

      // === COALESCING: FIND EARLIEST AND LATEST ===

      earlierTime <- earlierOf(meetingTime, noon)
      laterTime <- laterOf(meetingTime, noon)
      stdout.println(`Earlier: ${earlierTime}`)
      stdout.println(`Later: ${laterTime}`)

      // === SORTED TIME LIST VIA STREAM ===

      times <- List() of Time
      times += afternoon
      times += meetingTime
      times += noon

      sorted <- cat times | sort | collect as List of Time
      stdout.println(`Sorted times: ${sorted}`)

      // === STRING AND ISSET ===

      timeStr <- $meetingTime
      stdout.println(`Time string: ${timeStr}`)
      require meetingTime?

      unsetTime <- Time()
      require ~unsetTime?

Java: LocalTime.isBefore(), compareTo(). Python: direct operators on datetime.time. Rust: PartialOrd on chrono::NaiveTime. Go: no built-in time-of-day type, use time.Time methods. EK9: direct < > == operators on Time literals, consistent with all types.

afternoon before clock compare morning noon operator temporal time

time comparison
time operators
temporal check

<- rtn as Boolean: check.isBefore(against)

Explanation: Time has no isBefore() method; compare Time values directly with the < operator. See ek9 -h E50060 for details.


Question: How do I compare two durations and find the shorter one using coalescing?

Compare Duration values and pick the lesser using <? in EK9.

I need to select the shorter of two time periods for a break schedule.

In Java I used Duration.compareTo — what does EK9 use instead?

Answer:
EK9 Duration uses ISO 8601 literal syntax and supports all comparison and coalescing operators.

DURATION LITERALS
  shortBreak <- PT15M       15 minutes
  longBreak <- PT1H         1 hour
  halfDay <- PT4H30M        4 hours 30 minutes
  threeDays <- P3D          3 days

COMPARISON
  if shortBreak < longBreak     shorter duration
  ordering <- shortBreak <=> longBreak

COALESCING
  shorter <- shortBreak <? longBreak   returns the shorter duration
  longer <- shortBreak >? longBreak    returns the longer duration

The <? operator handles unset values safely: if one side is unset, it returns whichever is valid.

See Q542 for temporal comparison. See Q243 for coalescing operators. See Q540 for duration arithmetic.

defines module qa.operators.durationops

  defines function

    shorterOf() as pure
      ->
        left as Duration
        right as Duration
      <- rtn as Duration: left <? right

    longerOf() as pure
      ->
        left as Duration
        right as Duration
      <- rtn as Duration: left >? right

  defines program

    DurationOpsDemo()
      stdout <- Stdout()

      // === DURATION LITERALS (ISO 8601) ===

      shortBreak <- PT15M
      longBreak <- PT1H
      halfDay <- PT4H30M

      // === COMPARISON ===

      if shortBreak < longBreak
        stdout.println("Short break is shorter than long break")

      require shortBreak < longBreak
      require longBreak > shortBreak
      require shortBreak <> longBreak

      // === SPACESHIP ORDERING ===

      ordering <- shortBreak <=> longBreak
      stdout.println(`Short <=> Long: ${ordering}`)
      require ordering < 0

      // === COALESCING: FIND SHORTER AND LONGER ===

      shorter <- shorterOf(shortBreak, longBreak)
      longer <- longerOf(shortBreak, longBreak)
      stdout.println(`Shorter: ${shorter}`)
      stdout.println(`Longer: ${longer}`)
      require shorter == shortBreak
      require longer == longBreak

      // === HALF DAY COMPARISON ===

      require halfDay > longBreak
      require halfDay < PT5H
      stdout.println(`Half day: ${halfDay}`)

      // === STRING AND ISSET ===

      durStr <- $shortBreak
      stdout.println(`Duration string: ${durStr}`)
      require shortBreak?

      unsetDuration <- Duration()
      require ~unsetDuration?

Java: Duration.compareTo(), no coalescing. Python: timedelta comparison with < >. Rust: Duration::cmp(). Go: time.Duration comparison with < >. EK9: ISO 8601 literals (PT15M, P3D), direct operators, <? coalescing for safe minimum selection.

coalescing compare duration iso8601 lesser longer operator period shorter time

duration comparison
coalescing operators
duration literals

Explanation: Duration has no min() method, so left.min(right) is unresolved — use the <? coalescing operator to select the lesser value. See ek9 -h E50060 for details.


Question: How do I compare two money amounts and find the cheaper price in EK9?

Compare two Money values and select the lesser amount.

I need to find the cheapest item from two prices using operators.

In Java I used BigDecimal.compareTo for money — what does EK9 use?

Answer:
EK9 Money uses the same comparison and coalescing operators as every other type. Literals use amount#CURRENCY format.

MONEY LITERALS
  priceA <- 29.99#USD
  priceB <- 45.50#USD

COMPARISON
  if priceA < priceB          cheaper price
  ordering <- priceA <=> priceB  negative = cheaper, zero = same, positive = more expensive

COALESCING
  cheaper <- priceA <? priceB    returns the lower price
  dearer <- priceA >? priceB     returns the higher price

IMPORTANT: Comparison only works between same-currency Money values. Comparing 10#GBP with 20#USD returns unset — EK9 prevents accidental cross-currency comparison.

See Q35 for Money basics. See Q149 for currency safety. See Q243 for coalescing operators.

defines module qa.operators.moneycompare

  defines function

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

    dearerOf() as pure
      ->
        left as Money
        right as Money
      <- rtn as Money: left >? right

  defines program

    MoneyCompareDemo()
      stdout <- Stdout()

      // === MONEY LITERALS ===

      priceA <- 29.99#USD
      priceB <- 45.50#USD
      sameAsA <- 29.99#USD

      // === COMPARISON ===

      if priceA < priceB
        stdout.println("Price A is cheaper")

      require priceA < priceB
      require priceB > priceA
      require priceA == sameAsA
      require priceA <> priceB

      // === SPACESHIP ORDERING ===

      ordering <- priceA <=> priceB
      stdout.println(`PriceA <=> PriceB: ${ordering}`)
      require ordering < 0

      // === COALESCING: FIND CHEAPER AND DEARER ===

      cheaper <- cheaperOf(priceA, priceB)
      dearer <- dearerOf(priceA, priceB)
      stdout.println(`Cheaper: ${cheaper}`)
      stdout.println(`Dearer: ${dearer}`)
      require cheaper == priceA
      require dearer == priceB

      // === STRING CONVERSION ===

      priceStr <- $priceA
      stdout.println(`Price as string: ${priceStr}`)

      // === ISSET ===

      require priceA?
      unsetMoney <- Money()
      require ~unsetMoney?

      // === COPY OPERATOR ===

      backup <- 0.00#USD
      backup :=: priceA
      require backup == priceA
      stdout.println(`Copied price: ${backup}`)

Java: BigDecimal.compareTo(), no literal syntax, manual RoundingMode. Python: decimal.Decimal comparison. Rust: no built-in money type. Go: no built-in money type. EK9: 29.99#USD literals, direct < > == operators, <? coalescing, automatic HALF_UP rounding, cross-currency safety.

cheaper coalescing compare cost currency gbp money operator price usd

money comparison
price comparison
money operators

Explanation: EK9 has no Math type, so Math.min will not resolve (E50001); use the <? coalescing operator to select the lesser value. See ek9 -h E50001 for details.


Question: How do I check if two colours are equal and convert a colour to its string representation?

Compare two Colour values for equality in EK9.

I need to verify whether a pixel matches a target colour.

In CSS I compare hex codes as strings — how does EK9 compare colours?

Answer:
EK9 Colour uses hex literals (#RRGGBB) and supports equality, comparison, and string conversion operators.

COLOUR LITERALS
  primary <- #FF0000       red
  other <- #FF0000         same red
  accent <- #00FF00        green

EQUALITY
  if primary == other          same colour
  if primary <> accent         different colour

STRING CONVERSION
  str <- $primary              converts to string representation

COMPARISON
  ordering <- primary <=> accent  spaceship comparison
  brighter <- primary >? accent   coalescing for greater

Colour also supports arithmetic (+ for blend, - for remove) and HSL manipulation (withHue, withSaturation, withLightness). Use 'ek9 -h Colour' for the full API.

See Q34 for Colour basics. See Q238 for the complete operator set.

defines module qa.operators.colourequality

  defines program

    ColourEqualityDemo()
      stdout <- Stdout()

      // === COLOUR LITERALS ===

      primary <- #FF0000
      other <- #FF0000
      accent <- #00FF00
      highlight <- #0000FF

      // === EQUALITY ===

      if primary == other
        stdout.println("Primary and other are the same colour")

      require primary == other
      require primary <> accent
      require accent <> highlight

      // === SPACESHIP ORDERING ===

      ordering <- primary <=> accent
      stdout.println(`Red <=> Green: ${ordering}`)

      // === STRING CONVERSION ===

      redStr <- $primary
      greenStr <- $accent
      stdout.println(`Red as string: ${redStr}`)
      stdout.println(`Green as string: ${greenStr}`)

      // === COALESCING ===

      lesser <- primary <? accent
      greater <- primary >? accent
      stdout.println(`Lesser colour: ${lesser}`)
      stdout.println(`Greater colour: ${greater}`)

      // === ISSET ===

      require primary?
      unsetColour <- Colour()
      require ~unsetColour?

      // === COPY ===

      copied <- Colour()
      copied :=: primary
      require copied == primary
      stdout.println(`Copied colour: ${copied}`)

Java: java.awt.Color.equals(), getRGB() for comparison. Python: no built-in, tuple comparison. CSS: string comparison of hex codes. JavaScript: no built-in colour type. EK9: #RRGGBB literals, direct == <> operators, $ for string, arithmetic blending.

color colour compare conversion equality hex match operator rgb string

colour equality
colour comparison
colour string

Explanation: Colour has no equals() method. EK9 uses the == operator for equality checks on all types. See ek9 -h E50060 for details.


Question: How do I compare two dimensions and determine which is longer?

Compare two Dimension values to find the larger measurement.

I need to check whether width exceeds height for a layout calculation.

In CSS I compare pixel values as numbers — how does EK9 compare dimensions?

Answer:
EK9 Dimension uses unit-suffixed literals and supports comparison operators for same-unit values.

DIMENSION LITERALS
  width <- 2.5m
  height <- 1.8m

COMPARISON (SAME UNIT)
  if width > height          width is longer
  ordering <- width <=> height  spaceship comparison
  longer <- width >? height    coalescing for greater

IMPORTANT: Comparison only works between same-unit Dimensions. Comparing 2.5m with 10px returns unset — EK9 prevents unit mismatch errors at runtime.

Use convert() to change units before comparing across different unit systems.

See Q36 for Dimension basics. See Q238 for the complete operator set.

defines module qa.operators.dimensioncompare

  defines function

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

    lowerOf() as pure
      ->
        left as Dimension
        right as Dimension
      <- rtn as Dimension: left <? right

  defines program

    DimensionCompareDemo()
      stdout <- Stdout()

      // === DIMENSION LITERALS ===

      width <- 2.5m
      height <- 1.8m
      sameWidth <- 2.5m

      // === COMPARISON ===

      if width > height
        stdout.println("Width is longer than height")

      require width > height
      require height < width
      require width == sameWidth
      require width <> height

      // === SPACESHIP ORDERING ===

      ordering <- width <=> height
      stdout.println(`Width <=> Height: ${ordering}`)
      require ordering > 0

      // === COALESCING: FIND LONGER AND SHORTER ===

      longer <- higherOf(width, height)
      shorter <- lowerOf(width, height)
      stdout.println(`Longer: ${longer}`)
      stdout.println(`Shorter: ${shorter}`)
      require longer == width
      require shorter == height

      // === CROSS-UNIT RETURNS UNSET ===

      pixels <- 100px
      crossUnit <- width == pixels
      require ~crossUnit?
      stdout.println(`Cross-unit compare isSet: ${crossUnit?}`)

      // === STRING AND ISSET ===

      dimStr <- $width
      stdout.println(`Width as string: ${dimStr}`)
      require width?

      unsetDim <- Dimension()
      require ~unsetDim?

Java: plain double, manual unit tracking, no compile-time safety. F#: units of measure at compile time. CSS: string comparison. Python: no built-in dimension type. EK9: unit-suffixed literals (2.5m, 10px), same-unit comparison, cross-unit returns unset for safety.

compare dimension height layout longer measurement operator shorter unit width

dimension comparison
measurement operators
unit comparison

Explanation: Dimension has no getValue() method. EK9 uses comparison operators directly on Dimension values. Use #< to extract the numeric value if needed. See ek9 -h E50060 for details.


Question: How do I increment a character and compare two characters in EK9?

Increment a Character from 'a' to 'd' using ++ in EK9.

I need to iterate through characters by incrementing a letter variable.

In C I used char++ to walk through the alphabet — does EK9 support this?

Answer:
EK9 Character supports increment (++), decrement (--), comparison, and all standard operators.

CHARACTER LITERALS
  letter <- 'a'

INCREMENT
  letter++        moves from 'a' to 'b'
  letter++        moves from 'b' to 'c'
  letter++        moves from 'c' to 'd'

COMPARISON
  if letter == 'd'          exact match
  if letter > 'a'           ordering (alphabetical)
  ordering <- 'a' <=> 'z'  spaceship comparison

STRING CONVERSION
  str <- $letter            character to string

Character uses the same operators as Integer, Date, Money — the pattern is universal across all EK9 types.

See Q238 for the complete operator set. See Q23 for basic types.

defines module qa.operators.characterops

  defines function

    <?- Compare two characters and return spaceship ordering. -?>
    compareChars() as pure
      ->
        left as Character
        right as Character
      <- rtn as Integer: left <=> right

    <?- Check if two characters are equal. -?>
    charsEqual() as pure
      ->
        left as Character
        right as Character
      <- rtn as Boolean: left == right

  defines program

    CharacterOpsDemo()
      stdout <- Stdout()

      // === CHARACTER LITERAL ===

      letter <- 'a'
      stdout.println(`Starting letter: ${letter}`)

      // === INCREMENT ===

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

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

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

      // === COMPARISON VIA FUNCTION ===

      targetLetter <- 'd'
      matched <- charsEqual(letter, targetLetter)
      stdout.println(`Letter equals target: ${matched}`)

      // === SPACESHIP ORDERING VIA FUNCTION ===

      ordering <- compareChars(letter, targetLetter)
      stdout.println(`letter <=> target: ${ordering}`)

      alphaOrder <- compareChars('a', 'z')
      stdout.println(`'a' <=> 'z': ${alphaOrder}`)

      // === DECREMENT ===

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

      // === STRING CONVERSION ===

      charStr <- $letter
      stdout.println(`Character as string: ${charStr}`)

      // === ISSET ===

      require letter?
      unsetChar <- Character()
      require ~unsetChar?

      // === SORTED CHARACTER LIST ===

      chars <- List() of Character
      chars += 'z'
      chars += 'a'
      chars += 'm'

      sorted <- cat chars | sort | collect as List of Character
      stdout.println(`Sorted chars: ${sorted}`)

Java: char is a primitive integer type, char++ works. Python: no char type, ord()/chr() functions. Rust: char is Unicode scalar, no increment. Go: rune is int32, rune++ works. EK9: Character is a full type with ++, --, comparison, $, ? operators.

alphabet char character compare decrement increment letter operator walk

character increment
character comparison
character operators

Explanation: Character does not support addition with Integer. Use the ++ operator to increment a Character to the next Unicode code point. See ek9 -h E01072 for details.


Question: How do I compare screen resolutions and find the higher one in EK9?

Compare two Resolution values and pick the greater using >? in EK9.

I need to select the higher DPI for print output from two options.

How do I work with Resolution literals like 96dpi and 300dpi?

Answer:
EK9 Resolution uses DPI/PPI literals and supports all comparison and coalescing operators.

RESOLUTION LITERALS
  screenDpi <- 96dpi
  printDpi <- 300dpi

COMPARISON
  if printDpi > screenDpi       higher resolution
  ordering <- screenDpi <=> printDpi  spaceship comparison

COALESCING
  higherDpi <- screenDpi >? printDpi   returns the higher resolution
  lowerDpi <- screenDpi <? printDpi    returns the lower resolution

Resolution works with the same operators as Integer, Date, Money, Dimension — EK9 operators are universal across types.

See Q23 for basic types. See Q238 for the complete operator set.

defines module qa.operators.resolutioncompare

  defines function

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

    lowerOf() as pure
      ->
        left as Resolution
        right as Resolution
      <- rtn as Resolution: left <? right

  defines program

    ResolutionCompareDemo()
      stdout <- Stdout()

      // === RESOLUTION LITERALS ===

      screenDpi <- 96dpi
      printDpi <- 300dpi
      retinaDisplay <- 220dpi

      // === COMPARISON ===

      if printDpi > screenDpi
        stdout.println("Print has higher resolution than screen")

      require printDpi > screenDpi
      require screenDpi < printDpi
      require screenDpi <> printDpi

      // === SPACESHIP ORDERING ===

      ordering <- screenDpi <=> printDpi
      stdout.println(`Screen <=> Print: ${ordering}`)
      require ordering < 0

      // === COALESCING: FIND HIGHER AND LOWER ===

      higherDpi <- higherOf(screenDpi, printDpi)
      lowerDpi <- lowerOf(screenDpi, printDpi)
      stdout.println(`Higher DPI: ${higherDpi}`)
      stdout.println(`Lower DPI: ${lowerDpi}`)
      require higherDpi == printDpi
      require lowerDpi == screenDpi

      // === RETINA COMPARISON ===

      require retinaDisplay > screenDpi
      require retinaDisplay < printDpi
      stdout.println(`Retina: ${retinaDisplay}`)

      // === STRING AND ISSET ===

      dpiStr <- $printDpi
      stdout.println(`Print DPI as string: ${dpiStr}`)
      require printDpi?

      unsetRes <- Resolution()
      require ~unsetRes?

Java: no built-in resolution type, use int with manual DPI tracking. Python: no built-in. CSS: dpi in media queries but not programmable. Go: no built-in. EK9: 96dpi literals, direct comparison operators, >? coalescing for safe maximum selection.

compare display dpi higher lower operator ppi print resolution screen

resolution comparison
DPI comparison
resolution operators

higherDpi <- Math.max(screenDpi, printDpi)

Explanation: There is no 'Math' type in EK9 so 'Math.max' does not resolve; use the '>?' coalescing operator to select the greater value. See ek9 -h E50001 for details.


Question: How do I compare two timeout values in milliseconds in EK9?

Compare Millisecond values to determine which timeout is shorter.

I need to select the shorter of two network timeouts.

In Java I compared long millisecond values — how does EK9 handle this?

Answer:
EK9 Millisecond is a built-in type with literal syntax and full operator support.

MILLISECOND LITERALS
  shortTimeout <- 250ms
  longTimeout <- 5000ms

COMPARISON
  if shortTimeout < longTimeout   shorter timeout
  ordering <- shortTimeout <=> longTimeout

COALESCING
  quickest <- shortTimeout <? longTimeout  returns the shorter
  slowest <- shortTimeout >? longTimeout   returns the longer

Millisecond supports the same operator set as Duration, Date, Integer — EK9's type system is consistent.

See Q542 for temporal comparison. See Q243 for coalescing operators.

defines module qa.operators.millisecondcompare

  defines function

    <?- Compare two millisecond values and return spaceship ordering. -?>
    compareMs() as pure
      ->
        left as Millisecond
        right as Millisecond
      <- rtn as Integer: left <=> right

    <?- Check if the first timeout is shorter than the second. -?>
    isShorter() as pure
      ->
        left as Millisecond
        right as Millisecond
      <- rtn as Boolean: left < right

    <?- Return the quicker of two timeouts using coalescing min. -?>
    quickerOf() as pure
      ->
        left as Millisecond
        right as Millisecond
      <- rtn as Millisecond: left <? right

    <?- Return the slower of two timeouts using coalescing max. -?>
    slowerOf() as pure
      ->
        left as Millisecond
        right as Millisecond
      <- rtn as Millisecond: left >? right

  defines program

    MillisecondCompareDemo()
      stdout <- Stdout()

      // === MILLISECOND LITERALS ===

      shortTimeout <- 250ms
      longTimeout <- 5000ms
      mediumTimeout <- 1500ms

      // === COMPARISON VIA FUNCTION ===

      shorter <- isShorter(shortTimeout, longTimeout)
      stdout.println(`Short is faster: ${shorter}`)

      // === SPACESHIP ORDERING VIA FUNCTION ===

      ordering <- compareMs(shortTimeout, longTimeout)
      stdout.println(`Short <=> Long: ${ordering}`)

      mediumOrder <- compareMs(mediumTimeout, longTimeout)
      stdout.println(`Medium <=> Long: ${mediumOrder}`)

      // === COALESCING: FIND QUICKEST AND SLOWEST ===

      quickest <- quickerOf(shortTimeout, longTimeout)
      slowest <- slowerOf(shortTimeout, longTimeout)
      stdout.println(`Quickest: ${quickest}`)
      stdout.println(`Slowest: ${slowest}`)

      // === SORTED TIMEOUT LIST VIA STREAM ===

      timeouts <- List() of Millisecond
      timeouts += longTimeout
      timeouts += shortTimeout
      timeouts += mediumTimeout

      sorted <- cat timeouts | sort | collect as List of Millisecond
      stdout.println(`Sorted timeouts: ${sorted}`)

      // === STRING AND ISSET ===

      msStr <- $shortTimeout
      stdout.println(`Timeout as string: ${msStr}`)
      require shortTimeout?

      unsetMs <- Millisecond()
      require ~unsetMs?

      // === COPY OPERATOR ===

      backup <- 0ms
      backup :=: shortTimeout
      stdout.println(`Copied timeout: ${backup}`)

Java: plain long for millis, System.currentTimeMillis(), no type safety. Python: float seconds, no millis type. Rust: Duration::from_millis(). Go: time.Duration(n * time.Millisecond). EK9: 250ms literals, direct comparison, type-safe millisecond operations.

compare duration latency longer millisecond ms network operator shorter timeout

millisecond comparison
timeout comparison
millisecond operators

Explanation: Millisecond values are already in milliseconds and support comparison operators directly — there is no 'toMillis()' method to resolve. See ek9 -h E50060 for details.


Question: How do I define an Event record with Date, Time, and String fields and compare two events?

Create a record with multiple built-in type fields and use default operators.

I need to build an Event record with temporal fields and compare instances.

In Java I implemented Comparable on a multi-field class — how does EK9 do this?

Answer:
Define a record with typed fields, a constructor, and 'default operator' to get field-by-field comparison, equality, copy, isSet, and string conversion.

RECORD DEFINITION
  Event
    eventDate as Date: Date()
    eventTime as Time: Time()
    title as String: String()
    constructor, then 'default operator' LAST

DEFAULT OPERATOR
  'default operator' generates ==, <>, <, >, <=, >=, <=>, $, ?, :=:, #? and more from the fields. It must be the LAST thing in the record body.

COMPARISON
  if eventA < eventB        field-by-field comparison
  if eventA == eventB       field-by-field equality

Records have public fields — no getters needed. Since every field (Date, Time, String) supports operators, the default operator works seamlessly across all of them.

See Q116 for default operator. See Q245 for custom type operators. See Q238 for the complete operator set.

defines module qa.operators.mixedtypeclass

  defines record

    Event
      eventDate as Date: Date()
      eventTime as Time: Time()
      title as String: String()

      Event() as pure
        ->
          theDate as Date
          theTime as Time
          theTitle as String
        this.eventDate :=: theDate
        this.eventTime :=: theTime
        this.title :=: theTitle

      default operator

  defines program

    MixedTypeClassDemo()
      stdout <- Stdout()

      // === CREATE EVENTS ===

      eventA <- Event(2024-03-15, 09:00, "Sprint Planning")
      eventB <- Event(2024-03-15, 14:00, "Retrospective")
      eventC <- Event(2024-06-01, 10:00, "Release Review")

      // === COMPARISON (field-by-field via default operator) ===

      if eventA < eventB
        stdout.println("Sprint Planning is before Retrospective")

      require eventA < eventB
      require eventB < eventC
      require eventA <> eventB

      // === EQUALITY ===

      sameAsA <- Event(2024-03-15, 09:00, "Sprint Planning")
      require eventA == sameAsA
      stdout.println(`Events equal: ${eventA == sameAsA}`)

      // === SPACESHIP ORDERING ===

      ordering <- eventA <=> eventC
      stdout.println(`Planning <=> Release: ${ordering}`)
      require ordering < 0

      // === STRING CONVERSION ===

      eventStr <- $eventA
      stdout.println(`Event: ${eventStr}`)

      // === ISSET ===

      require eventA?
      unsetEvent <- Event()
      require ~unsetEvent?

      // === COPY OPERATOR ===

      copied <- Event()
      copied :=: eventA
      require copied == eventA
      stdout.println(`Copied event: ${copied}`)

      // === COALESCING ===

      earlier <- eventA <? eventC
      later <- eventA >? eventC
      stdout.println(`Earlier event: ${earlier}`)
      stdout.println(`Later event: ${later}`)
      require earlier == eventA
      require later == eventC

Java: implement Comparable, override equals/hashCode/toString manually. Python: @dataclass with order=True. Rust: #[derive(Eq, Ord)]. Kotlin: data class. Go: manual comparison functions. EK9: 'default operator' auto-generates all operators from fields, one line replaces boilerplate.

compare constructor date default event fields mixed operator record string time

multi-field record
default operator
record comparison

      default operator

  defines program

default operator
      title as String: String()

      (fields after default operator)

Explanation: The 'default operator' must be the LAST declaration in the record body. Placing fields or methods after it triggers E06820. See ek9 -h E06820 for details.


Question: How do I guard a date lookup so the block only runs if a date is found?

Use an if guard with a Date return value in EK9.

I need to safely handle a function that might not return a valid deadline.

In Java I checked if the date was null — how does EK9 guard against unset dates?

Answer:
EK9 guard variables combine declaration with an isSet check. If the function returns an unset Date, the if body is skipped.

GUARD PATTERN
  if deadline <- findDeadline()
    stdout.println(deadline)      only runs if deadline is set
  else
    stdout.println("No deadline")  runs if deadline is unset

The guard works identically for Date, Time, Money, String, or any type with the ? operator. One universal pattern replaces null checks across all types.

FUNCTION RETURNING UNSET
  findDeadline()
    <- rtn <- Date()    Date() creates an unset Date

See Q74 for guard if. See Q75 for guard switch. See Q76 for guard for. See Q77 for guard while.

defines module qa.flow.guard.withdate

  defines function

    findDeadline()
      <- rtn <- Date()

    findActiveDeadline()
      <- rtn <- 2024-09-30

    findProjectEnd()
      <- rtn <- 2024-12-31

  defines program

    GuardWithDateDemo()
      stdout <- Stdout()

      // === GUARD WITH UNSET DATE ===

      if deadline <- findDeadline()
        stdout.println(`Deadline: ${deadline}`)
      else
        stdout.println("No deadline found")

      // === GUARD WITH SET DATE ===

      if deadline <- findActiveDeadline()
        stdout.println(`Active deadline: ${deadline}`)
      else
        stdout.println("No active deadline")

      // === GUARD WITH ADDITIONAL CONDITION ===

      today <- 2024-06-15
      if projectEnd <- findProjectEnd() with projectEnd > today
        stdout.println(`Project ends in the future: ${projectEnd}`)

      // === GUARD SCOPING ===
      // The guard variable is scoped to the if/else block
      // It does not leak into surrounding scope

      if dueDate <- findActiveDeadline()
        daysMessage <- `Due: ${dueDate}`
        stdout.println(daysMessage)

      // === MULTIPLE GUARDS IN SEQUENCE ===

      if first <- findActiveDeadline()
        if second <- findProjectEnd()
          if first < second
            stdout.println(`Active deadline is before project end`)

Java: if (date != null) { use(date); } — null check separate from declaration. Python: if date := get_date() (walrus, 3.8+, None only). Rust: if let Some(d) = find_deadline(). Go: if d := findDeadline(); d != nil. Kotlin: val d = findDeadline(); if (d != null). EK9: if deadline <- findDeadline() — one expression for declare + check + scope.

check control date deadline flow guard if lookup safe temporal unset

guard with date
temporal guard
date safety

deadline <- findDeadline()
      if deadline == null
        return
      stdout.println(deadline)

Explanation: EK9 has no null and no return statement. Use guard variables in if statements to combine declaration and isSet checking in one expression. See ek9 -h E01072 for details.


Question: How do I sort a list of dates in ascending order using a stream pipeline?

Sort Date values in a stream pipeline and collect the result.

I need to order a list of holidays chronologically using cat and sort.

In Java I used Stream.sorted() on LocalDate — what is the EK9 equivalent?

Answer:
EK9 stream pipelines sort any type that supports the <=> operator. Since Date has built-in comparison, sort works directly.

SORT DATES
  dates <- [2024-12-25, 2024-01-01, 2024-07-04]
  sorted <- cat dates | sort | collect as List of Date

The sort stage uses the type's <=> operator. No Comparator needed for built-in types.

COMBINED PIPELINE
  cat dates | sort | head 2 | collect as List of Date   first two chronologically

This works identically for Date, Time, Integer, String, Money, Duration — any type with <=> is sortable in a stream.

See Q235 for stream operations reference. See Q120 for sort details. See Q542 for date comparison.

defines module qa.streams.sortdates

  defines program

    StreamSortDatesDemo()
      stdout <- Stdout()

      // === DATE LIST ===

      dates <- [2024-12-25, 2024-01-01, 2024-07-04, 2024-03-17, 2024-11-28]
      stdout.println(`Original: ${dates}`)

      // === SORT ASCENDING ===

      sorted <- cat dates | sort | collect as List of Date
      stdout.println(`Sorted: ${sorted}`)

      // === FIRST TWO CHRONOLOGICALLY ===

      firstTwo <- cat dates | sort | head 2 | collect as List of Date
      stdout.println(`First two: ${firstTwo}`)

      // === LAST TWO CHRONOLOGICALLY ===

      lastTwo <- cat dates | sort | tail 2 | collect as List of Date
      stdout.println(`Last two: ${lastTwo}`)

      // === SORT AND DEDUPLICATE ===

      datesWithDuplicates <- [2024-01-01, 2024-07-04, 2024-01-01, 2024-12-25, 2024-07-04]
      unique <- cat datesWithDuplicates | sort | uniq | collect as List of Date
      stdout.println(`Unique sorted: ${unique}`)

      // === DIRECT OUTPUT ===

      stdout.println("All dates sorted:")
      cat dates | sort > stdout

Java: dates.stream().sorted().collect(Collectors.toList()). Python: sorted(dates). Rust: dates.sort(). Go: sort.Slice(dates, func). EK9: cat dates | sort | collect as List of Date — Unix pipe syntax, implicit Comparator from <=> operator.

ascending cat chronological collect date list order pipeline sort stream

stream sort
date sorting
stream pipeline

Explanation: List has no sort() method. EK9 uses stream pipelines for sorting: cat the list, pipe through sort, and collect the result. See ek9 -h E50030 for details.


Question: Calculate an end date from a start date and a duration gap on a Project class.

Define a Project with a start date and duration, then compute the end date.

I need a class that holds a Date and Duration and can calculate when the project finishes.

In Java I'd use LocalDate.plus(Period) — how does EK9 combine Date and Duration?

Given a project start date and a duration, show the projected completion date.

Answer:
Define a class with Date and Duration fields, accessor methods, and a computed endDate() method. Use the + operator on Date to add Duration and get the end date.

CLASS WITH DATE AND DURATION
  Project
    startDate <- Date()
    gap <- Duration()
    startDate() accessor for the start date field
    gap() accessor for the duration field
    endDate() computes startDate + gap

DATE ARITHMETIC
  Date supports operator + with Duration, returning a new Date:
    endDate <- 2024-01-15 + P2M   gives a date 2 months later

This uses a class (not a record) because endDate() is a computed method. Records cannot have methods, only constructors and operators.

See Q1186 for date comparison. See Q1188 for duration operations. See Q1175 for record definition.

defines module qa.classesandoop.projectenddate

  defines class

    Project
      startDate <- Date()
      gap <- Duration()

      default private Project() as pure

      Project() as pure
        ->
          theStart as Date
          theGap as Duration
        this.startDate :=: theStart
        this.gap :=: theGap

      startDate() as pure
        <- rtn as Date: Date(startDate)

      gap() as pure
        <- rtn as Duration: Duration(gap)

      endDate() as pure
        <- rtn as Date: startDate + gap

      default operator

  defines program

    ProjectEndDateDemo()
      stdout <- Stdout()

      // === PROJECT WITH 2-MONTH DURATION ===

      sprintProject <- Project(2024-01-15, P2M)
      stdout.println(`Sprint start: ${sprintProject.startDate()}`)
      stdout.println(`Sprint end: ${sprintProject.endDate()}`)

      // === PROJECT WITH 3-DAY DURATION ===

      hotfix <- Project(2024-06-01, P3D)
      stdout.println(`Hotfix start: ${hotfix.startDate()}`)
      stdout.println(`Hotfix end: ${hotfix.endDate()}`)

      // === PROJECT WITH 1-YEAR DURATION ===

      roadmap <- Project(2024-03-15, P1Y)
      stdout.println(`Roadmap start: ${roadmap.startDate()}`)
      stdout.println(`Roadmap end: ${roadmap.endDate()}`)

      // === COMPARE END DATES ===

      if hotfix.endDate() < sprintProject.endDate()
        stdout.println("Hotfix finishes before sprint project")

      if roadmap.endDate() > sprintProject.endDate()
        stdout.println("Roadmap extends beyond sprint project")

Java: LocalDate start; Period duration; start.plus(duration). Python: date + timedelta. Rust: NaiveDate + Duration (chrono). Go: t.Add(duration). EK9: startDate + gap — operator + on Date with Duration, no method calls.

add
arithmetic
class
constructor
date
duration
end date
operator plus
project

date duration arithmetic
class definition
operator +

defines record
      Project
        endDate()
          <- rtn as Date: startDate + gap

Explanation: Records cannot have methods, only constructors and operators. Use a class when you need methods like endDate(). See ek9 -h E07290 for details.


Question: Sort a list of Meeting records by date then by time using a stream pipeline.

Build a record with Date and Time fields and sort instances in a stream.

I need to order meetings chronologically using cat, sort, and collect.

In Java I'd use Comparator.comparing(Meeting::getDate).thenComparing(Meeting::getTime). What is the EK9 equivalent?

Given meetings on different dates and times, sort them into chronological order.

Answer:
Define a record with Date and Time fields, then use 'default operator' to get field-by-field comparison. Stream sort uses the <=> operator automatically.

RECORD DEFINITION
  Meeting with meetingDate, meetingTime, title fields
  'default operator' generates <=> comparing fields in declaration order (date first, then time, then title)

STREAM SORT
  sorted <- cat meetings | sort | collect as List of Meeting

The sort stage uses <=> from the default operator. Fields declared first have highest sort priority. Date before Time before title means chronological ordering happens naturally.

See Q1135 for record sort pipeline. See Q1195 for mixed-type record. See Q1197 for date sorting.

defines module qa.streams.sortmeetingsbydatetime

  defines record

    Meeting
      meetingDate as Date: Date()
      meetingTime as Time: Time()
      title as String: String()

      Meeting() as pure
        ->
          theDate as Date
          theTime as Time
          theTitle as String
        this.meetingDate :=: theDate
        this.meetingTime :=: theTime
        this.title :=: theTitle

      default operator

  defines program

    SortMeetingsByDateTimeDemo()
      stdout <- Stdout()

      // === CREATE MEETINGS ===

      meetings <- List() of Meeting
      meetings += Meeting(2024-03-15, 14:00, "Retrospective")
      meetings += Meeting(2024-03-15, 09:00, "Sprint Planning")
      meetings += Meeting(2024-03-14, 16:00, "Code Review")
      meetings += Meeting(2024-03-16, 10:30, "Demo Day")

      // === SORT BY DATE THEN TIME (field declaration order) ===

      sorted <- cat meetings | sort | collect as List of Meeting
      stdout.println("Sorted meetings:")
      cat sorted > stdout

      // === FIRST TWO MEETINGS ===

      firstTwo <- cat meetings | sort | head 2 | collect as List of Meeting
      stdout.println("First two meetings:")
      cat firstTwo > stdout

      // === VERIFY ORDERING ===

      earliest <- Meeting(2024-03-14, 16:00, "Code Review")
      latest <- Meeting(2024-03-16, 10:30, "Demo Day")
      require earliest < latest

Java: stream().sorted(Comparator.comparing(m -> m.date).thenComparing(m -> m.time)). Python: sorted(meetings, key=lambda m: (m.date, m.time)). Rust: sort_by_key with tuple. Go: sort.Slice with custom less function. EK9: declare fields in sort priority order + default operator + cat | sort | collect.

chronological
collect
date
default operator
meeting
pipeline
record
sort
stream
time

stream sort custom record
multi-field sort
date time ordering

Explanation: EK9 uses stream pipelines for sorting, not method calls. Define <=> via default operator and use cat | sort | collect. See ek9 -h E50060 for details.


Question: Sum the line totals of an order where each line has a quantity and unit price in Money.

Calculate order totals by multiplying Money by Integer quantities.

I need to compute the total cost of an order with multiple line items.

In Java I'd use BigDecimal.multiply for each line — how does EK9 handle Money arithmetic?

Given line items with quantity and unit price, sum up the order total.

Answer:
EK9 Money supports operator * with Integer for quantity multiplication and operator + for accumulation.

MONEY ARITHMETIC
  unitPrice <- 29.99#USD
  lineTotal <- unitPrice * quantity   Money * Integer = Money

ACCUMULATION
  Use a for loop with += to sum Money values:
  total <- 0.00#USD
  for item in items
    total += item.unitPrice * item.quantity

Money handles precision automatically with HALF_UP rounding. Same-currency operations work directly; cross-currency returns unset.

See Q1189 for money comparison. See Q35 for Money basics. See Q1139 for stream join.

defines module qa.operators.moneyordertotal

  defines record

    OrderLine
      description as String: String()
      unitPrice as Money: Money()
      quantity as Integer: Integer()

      OrderLine() as pure
        ->
          theDescription as String
          theUnitPrice as Money
          theQuantity as Integer
        this.description :=: theDescription
        this.unitPrice :=: theUnitPrice
        this.quantity :=: theQuantity

      default operator

  defines program

    MoneyOrderTotalDemo()
      stdout <- Stdout()

      // === CREATE ORDER LINES ===

      lines <- List() of OrderLine
      lines += OrderLine("Widget", 29.99#USD, 3)
      lines += OrderLine("Gadget", 45.50#USD, 2)
      lines += OrderLine("Cable", 9.99#USD, 5)

      // === CALCULATE LINE TOTALS ===

      for line in lines
        lineTotal <- line.unitPrice * line.quantity
        stdout.println(`${line.description}: ${lineTotal}`)

      // === SUM ALL LINE TOTALS ===

      orderTotal <- 0.00#USD
      for line in lines
        orderTotal += line.unitPrice * line.quantity

      stdout.println(`Order total: ${orderTotal}`)

      // === VERIFY INDIVIDUAL TOTALS ===

      widgetTotal <- 29.99#USD * 3
      stdout.println(`Widget total: ${widgetTotal}`)

      gadgetTotal <- 45.50#USD * 2
      stdout.println(`Gadget total: ${gadgetTotal}`)

Java: BigDecimal.multiply(quantity).setScale(2, RoundingMode.HALF_UP), then stream().reduce(BigDecimal.ZERO, BigDecimal::add). Python: Decimal * int. Rust: no built-in money. Go: no built-in money. EK9: unitPrice * quantity with automatic precision, += for accumulation.

accumulate
arithmetic
line item
money
multiply
order
price
quantity
sum
total

money arithmetic
order total calculation
money multiplication

Explanation: Money has no multiply() method — use the * operator directly on Money with an Integer or Float quantity. See ek9 -h E50060 for details.


Question: Check if a project has a deadline set, and warn if it is in the past.

Use a guard to safely handle an optional deadline and compare it with today.

I need to check whether a deadline exists and if so whether it has passed.

In Java I'd check if deadline != null && deadline.isBefore(today). How does EK9 do this?

Guard a date lookup and then compare the result against the current date.

Answer:
Use an if guard to declare the deadline variable and check isSet in one expression. If the function returns an unset Date, the body is skipped. Inside the guard, compare the deadline against today.

GUARD WITH COMPARISON
  if deadline <- findDeadline() with deadline < today
    stdout.println("Deadline has passed!")

The 'with' clause adds a condition to the guard: the body runs only if the value is set AND the condition is true. This replaces null checks and isBefore() in one expression.

See Q1196 for guard with date. See Q74 for guard if. See Q1186 for date comparison.

defines module qa.flow.guard.deadlinecheck

  defines function

    findDeadline()
      <- rtn <- Date()

    findActiveDeadline()
      <- rtn <- 2024-03-01

    findFutureDeadline()
      <- rtn <- 2025-12-31

  defines program

    GuardDeadlineCheckDemo()
      stdout <- Stdout()

      today <- 2024-06-15

      // === GUARD: NO DEADLINE SET ===

      if deadline <- findDeadline()
        stdout.println(`Deadline: ${deadline}`)
      else
        stdout.println("No deadline set for this project")

      // === GUARD: DEADLINE IN THE PAST ===

      if deadline <- findActiveDeadline()
        if deadline < today
          stdout.println(`Warning: deadline ${deadline} is in the past`)
        else
          stdout.println(`Deadline ${deadline} is still upcoming`)

      // === GUARD: DEADLINE IN THE FUTURE ===

      if deadline <- findFutureDeadline()
        if deadline > today
          stdout.println(`On track: deadline ${deadline} is in the future`)

      // === MULTIPLE DEADLINE CHECKS ===

      if first <- findActiveDeadline()
        if second <- findFutureDeadline()
          gap <- second - first
          stdout.println(`Gap between deadlines: ${gap}`)

Java: Date d = findDeadline(); if (d != null && d.isBefore(today)). Python: if (d := find_deadline()) and d < today. Rust: if let Some(d) = find_deadline() { if d < today }. Go: if d := findDeadline(); d != nil && d.Before(today). EK9: if deadline <- findDeadline() with deadline < today — one expression for declare + isSet + compare.

check
comparison
control flow
date
deadline
future
guard
past
today
with

guard with condition
date comparison
deadline checking

deadline <- findDeadline()
      if deadline != null
        if deadline.isBefore(today)
          stdout.println("Overdue")

Explanation: EK9 has no null and no isBefore() method. Use guard variables in if statements and comparison operators on Date. See ek9 -h E01072 for details.


Question: Calculate the time gap between two scheduled events.

Subtract two dates to find the duration between events.

I need to compute how long between a project start and its milestone.

In Java I'd use ChronoUnit.DAYS.between(). What is the EK9 equivalent?

Given two event dates, find the duration gap and compare it to a threshold.

Answer:
EK9 Date supports operator - with another Date, returning a Duration. Use Duration comparison to check if the gap meets a threshold.

DATE SUBTRACTION
  gap <- eventB.eventDate - eventA.eventDate   returns Duration

DURATION COMPARISON
  threshold <- P30D
  if gap > threshold
    stdout.println("Events are more than 30 days apart")

Component access on Duration:
  gap.days()    number of days
  gap.months()  number of months

See Q1174 for duration between dates. See Q1188 for duration operations. See Q1186 for date comparison.

defines module qa.operators.durationbetweenevents

  defines record

    ScheduledEvent
      eventName as String: String()
      eventDate as Date: Date()

      ScheduledEvent() as pure
        ->
          theName as String
          theDate as Date
        this.eventName :=: theName
        this.eventDate :=: theDate

      default operator

  defines program

    DurationBetweenEventsDemo()
      stdout <- Stdout()

      // === CREATE EVENTS ===

      launch <- ScheduledEvent("Product Launch", 2024-01-15)
      milestone <- ScheduledEvent("Q2 Milestone", 2024-06-30)
      review <- ScheduledEvent("Annual Review", 2024-12-31)

      // === CALCULATE GAPS ===

      launchToMilestone <- milestone.eventDate - launch.eventDate
      stdout.println(`Launch to Milestone: ${launchToMilestone}`)
      stdout.println(`Days: ${launchToMilestone.days()}`)

      milestoneToReview <- review.eventDate - milestone.eventDate
      stdout.println(`Milestone to Review: ${milestoneToReview}`)

      // === COMPARE DURATIONS ===

      if launchToMilestone < milestoneToReview
        stdout.println("First half is shorter than second half")

      // === CHECK AGAINST THRESHOLD ===

      threshold <- P90D
      if launchToMilestone > threshold
        stdout.println(`Launch to milestone exceeds ${threshold}`)

      // === TOTAL SPAN ===

      totalSpan <- review.eventDate - launch.eventDate
      stdout.println(`Total span: ${totalSpan}`)

Java: ChronoUnit.DAYS.between(d1, d2) or Period.between(). Python: (d2 - d1).days. Rust: d2.signed_duration_since(d1). Go: t2.Sub(t1). EK9: eventB.eventDate - eventA.eventDate gives Duration directly, then compare with < > operators.

between compare date days duration events gap schedule subtract threshold

date subtraction
duration comparison
event gap

stdout.println(`Launch to milestone exceeds ${launch.eventDate.between(review.eventDate)}`)

Explanation: EK9 Date has no between() method (and no ChronoUnit) — subtract two Dates with the - operator to get a Duration. See ek9 -h E50060 for details.


Question: Filter a product list to items over 50 USD and sort by price ascending.

Use a stream pipeline to filter products by price and sort the result.

I need to find all expensive items from a list and display them in order.

In Java I'd use stream().filter(p -> p.getPrice() > 50).sorted(). What is the EK9 equivalent?

Given products with Money prices, filter by a named threshold and sort.

Answer:
Define a pure predicate function for the filter and a named constant for the threshold. Stream pipeline: cat | filter by | sort | collect.

NAMED THRESHOLD
  threshold <- 50.00#USD    named constant avoids magic literal

PREDICATE FUNCTION
  isExpensive() as pure
    -> item as Product
    <- rtn as Boolean: item.price > threshold

PIPELINE
  expensive <- cat products | filter by isExpensive | sort | collect as List of Product

The filter stage applies the predicate. The sort stage uses <=> from default operator. Named constants make the threshold explicit and changeable.

See Q1134 for filter-map-sort. See Q1189 for money comparison. See Q1184 for named constants.

defines module qa.streams.filterexpensiveproducts

  defines record

    Product
      productName as String: String()
      price as Money: Money()

      Product() as pure
        ->
          theName as String
          thePrice as Money
        this.productName :=: theName
        this.price :=: thePrice

      default operator

  defines function

    isExpensive() as pure
      -> item as Product
      <- rtn as Boolean?
      threshold <- 50.00#USD
      rtn: item.price > threshold

  defines program

    FilterExpensiveProductsDemo()
      stdout <- Stdout()

      // === CREATE PRODUCT LIST ===

      products <- List() of Product
      products += Product("Mouse", 19.99#USD)
      products += Product("Keyboard", 49.99#USD)
      products += Product("Monitor", 299.99#USD)
      products += Product("Headset", 79.50#USD)
      products += Product("Cable", 9.99#USD)
      products += Product("Webcam", 65.00#USD)

      // === FILTER AND SORT ===

      expensive <- cat products | filter by isExpensive | sort | collect as List of Product
      stdout.println("Expensive products (sorted):")
      cat expensive > stdout

      // === DIRECT OUTPUT ===

      stdout.println("Expensive to stdout:")
      cat products | filter by isExpensive | sort > stdout

Java: stream().filter(p -> p.getPrice().compareTo(threshold) > 0).sorted(). Python: [p for p in products if p.price > 50]. Rust: iter().filter(|p| p.price > 50).sorted(). Go: manual loop with append. EK9: cat products | filter by isExpensive | sort | collect — pipe syntax with named predicate.

expensive
filter
money
named constant
pipeline
predicate
product
sort
stream
threshold

stream filter with money
named threshold
product filtering

expensive <- products.stream().filter(p -> p.price > 50).collect()

Explanation: EK9 uses stream pipelines with named predicate functions, not lambda expressions or method calls. See ek9 -h E50060 for details.


Question: Define an Employee record with name, hire date, and department, then sort employees by hire date.

Create an Employee record with a Date field and sort a list of employees chronologically.

I need to define an employee type and order employees by when they were hired.

In Java I'd use Comparator.comparing(Employee::getHireDate). How does EK9 sort by a date field?

Build an Employee record with hire date and use a stream pipeline to sort by seniority.

Answer:
Define a record with fields in the desired sort priority order. The 'default operator' generates <=> comparing fields in declaration order. Put hireDate first for chronological sorting.

FIELD ORDER MATTERS
  Employee
    hireDate as Date: Date()     first field = primary sort key
    employeeName as String: String()  second field = secondary sort key
    department as String: String()    third field = tertiary sort key

SORT BY HIRE DATE
  sorted <- cat employees | sort | collect as List of Employee

Since hireDate is declared first, the default <=> operator sorts chronologically. Stream pipelines use the type's <=> operator automatically.

See Q1199 for sorting by date+time. See Q1135 for record sort pipeline. See Q1195 for mixed-type record.

defines module qa.classesandoop.employeehiredate

  defines record

    Employee
      hireDate as Date: Date()
      employeeName as String: String()
      department as String: String()

      Employee() as pure
        ->
          theHireDate as Date
          theName as String
          theDepartment as String
        this.hireDate :=: theHireDate
        this.employeeName :=: theName
        this.department :=: theDepartment

      default operator

  defines program

    EmployeeHireDateDemo()
      stdout <- Stdout()

      // === CREATE EMPLOYEES ===

      employees <- List() of Employee
      employees += Employee(2024-06-15, "Alice", "Engineering")
      employees += Employee(2023-01-10, "Bob", "Marketing")
      employees += Employee(2024-01-05, "Carol", "Engineering")
      employees += Employee(2022-09-20, "Dave", "Operations")

      // === SORT BY HIRE DATE (field declaration order) ===

      sorted <- cat employees | sort | collect as List of Employee
      stdout.println("Employees by seniority:")
      cat sorted > stdout

      // === MOST SENIOR (earliest hire date) ===

      mostSenior <- cat employees | sort | head 1 | collect as List of Employee
      stdout.println("Most senior:")
      cat mostSenior > stdout

      // === NEWEST HIRES ===

      newest <- cat employees | sort | tail 2 | collect as List of Employee
      stdout.println("Newest hires:")
      cat newest > stdout

      // === COMPARE TWO EMPLOYEES ===

      alice <- Employee(2024-06-15, "Alice", "Engineering")
      bob <- Employee(2023-01-10, "Bob", "Marketing")
      if bob < alice
        stdout.println("Bob was hired before Alice")

Java: employees.stream().sorted(Comparator.comparing(Employee::getHireDate)). Python: sorted(employees, key=lambda e: e.hire_date). Rust: sort_by_key(|e| e.hire_date). Go: sort.Slice with custom less. EK9: declare hireDate first + default operator + cat | sort | collect.

chronological
date
default operator
employee
field order
hire date
record
seniority
sort
stream

sort by date field
record field order
employee sorting

default operator
      department as String: String()

      (fields after default operator)


Question: Check if a Time value falls within business hours (09:00 to 17:00).

Validate that a time is between start and end of business using is in.

I need to determine whether a meeting time falls within office hours.

In Java I'd check time.isAfter(start) && time.isBefore(end). What is the EK9 equivalent?

Given named time boundaries, check if a current time is within the range.

Answer:
EK9 Time supports the 'is in' operator with a range for clean boundary checks. Use named constants for the boundaries.

NAMED BOUNDARIES
  businessStart <- 09:00
  businessEnd <- 17:00

RANGE CHECK
  if currentTime is in businessStart ... businessEnd
    stdout.println("Within business hours")

The 'is in start ... end' syntax is inclusive on both ends. It works with any comparable type: Time, Date, Integer, Float, etc. Named constants avoid magic literals and make the range self-documenting.

See Q1102 for is in with ranges. See Q1187 for time comparison. See Q1184 for named constants.

defines module qa.operators.timerangecheck

  defines program

    TimeRangeCheckDemo()
      stdout <- Stdout()

      // === NAMED TIME BOUNDARIES ===

      businessStart <- 09:00
      businessEnd <- 17:00

      // === WITHIN BUSINESS HOURS ===

      morningMeeting <- 10:30
      if morningMeeting is in businessStart ... businessEnd
        stdout.println(`${morningMeeting} is within business hours`)

      // === OUTSIDE BUSINESS HOURS ===

      earlyCall <- 07:45
      if not (earlyCall is in businessStart ... businessEnd)
        stdout.println(`${earlyCall} is outside business hours`)

      // === ON THE BOUNDARY (INCLUSIVE) ===

      nineSharp <- 09:00
      fivePm <- 17:00
      startInRange <- nineSharp is in businessStart ... businessEnd
      endInRange <- fivePm is in businessStart ... businessEnd
      stdout.println(`09:00 in range: ${startInRange}`)
      stdout.println(`17:00 in range: ${endInRange}`)

      // === MULTIPLE TIME CHECKS ===

      times <- [08:00, 09:30, 12:00, 16:45, 18:30]
      for checkTime in times
        if checkTime is in businessStart ... businessEnd
          stdout.println(`${checkTime}: office hours`)
        else
          stdout.println(`${checkTime}: after hours`)

Java: !time.isBefore(start) && !time.isAfter(end) — two method calls, easy to get wrong. Python: start <= t <= end — chained comparison. Rust: (start..=end).contains(&t). Go: !t.Before(start) && !t.After(end). EK9: time is in start ... end — one readable expression, inclusive bounds.

boundary
business hours
check
inclusive
is in
named constant
range
schedule
time
validate

time range check
is in with time
named constants


Question: Categorise a dimension measurement as small, medium, or large using named thresholds.

Use if-else with Dimension comparisons and named constants to classify a measurement.

I need to bucket a measurement into size categories using named thresholds.

In Java I'd use if-else chains with comparisons to classify dimensions. What is the EK9 approach?

Given a Dimension value, classify it into small, medium, or large with clear boundaries.

Answer:
Use named Dimension constants for thresholds and if-else chains for classification. Named constants make the boundaries self-documenting.

NAMED THRESHOLDS
  smallLimit <- 1.0m
  largeLimit <- 5.0m

CLASSIFICATION
  if measurement < smallLimit
    category: "small"
  else if measurement < largeLimit
    category: "medium"
  else
    category: "large"

Dimension supports comparison operators (<, >, <=, >=) for same-unit values. Named constants avoid magic literals and make the classification logic readable.

See Q1191 for dimension comparison. See Q1184 for named constants. See Q62 for if-else chains.

defines module qa.flow.switchoncategory

  defines function

    shorterOf() as pure
      ->
        left as Dimension
        right as Dimension
      <- rtn as Dimension: left <? right

    longerOf() as pure
      ->
        left as Dimension
        right as Dimension
      <- rtn as Dimension: left >? right

    classify() as pure
      -> measurement as Dimension
      <- category as String: "unknown"
      smallLimit <- 1.0m
      largeLimit <- 5.0m
      if measurement < smallLimit
        category: "small"
      else if measurement < largeLimit
        category: "medium"
      else
        category: "large"

  defines program

    SwitchOnCategoryDemo()
      stdout <- Stdout()

      // === CLASSIFY MEASUREMENTS ===

      measurements <- [0.5m, 1.0m, 2.5m, 5.0m, 10.0m]
      for measurement in measurements
        category <- classify(measurement)
        stdout.println(`${measurement} -> ${category}`)

      // === COALESCING COMPARISON ===

      beamA <- 0.5m
      beamB <- 2.5m
      shortest <- shorterOf(beamA, beamB)
      longest <- longerOf(beamA, beamB)
      stdout.println(`Shortest: ${shortest}`)
      stdout.println(`Longest: ${longest}`)

      // === STRING REPRESENTATION ===

      panel <- 2.5m
      panelStr <- $panel
      stdout.println(`Panel dimension: ${panelStr}`)

Java: if (dim < 1.0) small; else if (dim < 5.0) medium; else large — plain doubles, no units. Python: similar if-else, no unit type. Rust: match with guards. Go: if-else with float64. EK9: if-else with Dimension comparison, unit-safe, named constants for thresholds.

categorise
classify
dimension
if else
large
measurement
medium
named constant
small
threshold

dimension classification
named thresholds
if-else categorisation

Explanation: Dimension supports comparison operators directly. No getValue() method needed. Use named Dimension constants for thresholds instead of magic numeric literals. See ek9 -h E50060 for details.