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

← Operators and Expressions · Ref: Q1195

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.

Example

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

Common mistakes

E06820 — 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.

Incorrect:

default operator
      title as String: String()

      (fields after default operator)

Correct:

      default operator

  defines program
Other ways to ask this
  • 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?

Coming from another language?

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.

Keywords: record, default, operator, string, event, compare, fields, date, mixed, constructor, time