Can a trait define comparison and hashcode operators in EK9?

← Operators and Expressions · Ref: Q1051

Yes, traits can define operator signatures that implementing classes must provide. A trait can declare abstract operators or provide default implementations.

Abstract operator in a trait (no body):

  defines trait
    Rankable
      operator <=> as pure
        -> other as Rankable
        <- rtn as Integer?

Default operator in a trait (with body):

  defines trait
    Identifiable
      operator #? as pure
        <- rtn as Integer: 0

Any class 'with trait of Rankable' must implement <=>. The trait contract guarantees that all Rankable objects can be compared.

This pattern is useful for:
- Defining sortable contracts
- Ensuring all types in a collection support comparison
- Building generic algorithms that depend on ordering

Note: override is required when implementing trait operators in a class.

See Q1049 for comparison operators on records. See Q1050 for coalescing on classes. See Q210 for trait delegation.

Example

defines module qa.operators.operatorsontrait

  defines trait

    Rankable
      rank() as pure
        <- rtn as Integer?

  defines class

    Task with trait of Rankable
      priority as Integer: 0
      taskName as String: String()

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

      override rank() as pure
        <- rtn as Integer: priority

      operator <=> as pure
        -> other as Task
        <- rtn as Integer: priority <=> other.priority

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

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

      operator #? as pure
        <- rtn as Integer: #? priority

      override operator ? as pure
        <- rtn as Boolean: taskName? and priority?

      operator $ as pure
        <- rtn as String: `${taskName}(${priority})`

  defines program

    TraitOperatorsDemo()
      stdout <- Stdout()

      taskA <- Task("Fix bug", 3)
      taskB <- Task("Deploy", 8)

      stdout.println(`A < B: ${taskA < taskB}`)
      stdout.println(`A > B: ${taskA > taskB}`)
      stdout.println(`A hash: ${#? taskA}`)

      //Coalescing: get higher priority
      urgent <- taskA >? taskB
      stdout.println(`More urgent: ${urgent}`)

Common mistakes

E05120 — Overriding an inherited trait member (here rank() from trait Rankable) requires the 'override' keyword, otherwise the compiler reports E05120. See ek9 -h E05120 for details.

Incorrect:

rank() as pure

Correct:

override rank() as pure
Other ways to ask this
  • Show me operator definitions inside a trait
  • Do traits support #? and comparison operators?
  • Define a sortable contract using a trait with operators

Coming from another language?

Java: Comparable interface. Rust: Ord trait. Python: __lt__ protocol. EK9: trait with operator signatures.

Keywords: hashcode, trait, comparison, operator, abstract, contract