Can a trait define comparison and hashcode operators in EK9?

← Operators and Expressions · Ref: Q1051

Yes. A trait can declare the SAMENESS of every type that implements it: a <=> (or ==) with a body plus a matching #?. Sameness must have a body where it is declared - a bodiless (abstract) <=> in a trait is E07183 - so the trait writes the body over its own abstract accessors, and implementers supply those accessors, not an override of <=>.

  defines trait
    Rankable
      rank() as pure
        <- rtn as Integer?
      operator <=> as pure
        -> other as Rankable
        <- rtn as Integer: rank() <=> other.rank()
      operator #? as pure
        <- rtn as Integer: #?rank()

Any class 'with trait of Rankable' implements rank() and inherits the sameness. The compiler generates <, >, <=, >=, == and <> from the trait's <=> (writing them is E07181), and the sameness is sealed: an implementer may not redeclare <=>, == or #? (E07182/E07184). The trait contract guarantees that all Rankable objects can be compared, sorted and used with <? and >?.

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

A different ordering for one use (say by name) is a comparator function given to sort, never a second <=>.

Note: override is required when implementing the trait's accessor in a class (override rank()).

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

Example

#!ek9
defines module qa.operators.operatorsontrait

  defines trait

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

      //The sameness, written once over the trait's own abstract accessor.
      //<, >, <=, >=, == and <> with any Rankable are generated from it.
      operator <=> as pure
        -> other as Rankable
        <- rtn as Integer: rank() <=> other.rank()

      operator #? as pure
        <- rtn as Integer: #?rank()

  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

      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

E07183 — Sameness must have a body where it is declared: a bodiless (abstract) <=> in a trait is E07183 (SAMENESS_MUST_BE_CONCRETE). Write the body over the trait's own abstract accessor (rank()); implementers supply rank(), not an override of <=>. See ek9 -h E07183 for details.

Incorrect:

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

Correct:

      operator <=> as pure
        -> other as Rankable
        <- rtn as Integer: rank() <=> other.rank()
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: a trait declares a <=> and #? with bodies over its abstract accessors; implementers supply the accessors.

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