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