How do I implement comparison operators on a record for sorting?
← Operators and Expressions · Ref: Q1049
Implement the <=> (spaceship) operator on your record - its sameness - with a matching #? that hashes the same fields. The compiler then GENERATES <, >, <=, >=, == and <> from <=>; you never write them (writing or requesting one is E07181), so they can never disagree with <=>. All comparison operators are pure.
defines record Priority level as Integer: 0
operator <=> as pure -> other as Priority <- rtn as Integer: level <=> other.level
operator #? as pure <- rtn as Integer: #?level
default operator
A hand-written <=> needs a hand-written #? (E07184); 'default operator' then supplies the rest ($, ?).
Once <=> is defined, you can sort lists and use coalescing operators:
smaller <- priorityA <? priorityB larger <- priorityA >? priorityB
The coalescing operators <? and >? return the lesser or greater value. They work because the record has <=>. For a different ordering (say by label), give sort a comparator function instead of writing a second <=>.
See Q1050 for coalescing operators on a class. See Q239 for comparison ordering. See Q1051 for operators on traits.
Example
defines module qa.operators.comparisonrecord defines record Priority level as Integer: 0 label as String: String() Priority() -> level as Integer label as String this.level :=: level this.label :=: label operator <=> as pure -> other as Priority <- rtn as Integer: level <=> other.level //A hand-written <=> needs a hand-written #? hashing the same field operator #? as pure <- rtn as Integer: #?level //<, >, <=, >=, == and <> are generated from <=>; this adds $ and ? default operator defines program ComparisonRecordDemo() stdout <- Stdout() high <- Priority(10, "Critical") medium <- Priority(5, "Normal") low <- Priority(1, "Low") stdout.println(`High > Low: ${high > low}`) stdout.println(`Medium <= High: ${medium <= high}`) //Coalescing: get the lesser/greater lesser <- high <? medium greater <- low >? medium stdout.println(`Lesser: ${lesser}`) stdout.println(`Greater: ${greater}`)
Common mistakes
E07181 — The < operator on a record with <=> is generated from <=> - writing it by hand is E07181 (COMPARISON_GENERATED_FROM_SAMENESS). Delete it: <, >, <=, >=, == and <> all come from <=>. See ek9 -h E07181 for details.
Incorrect:
operator < as pure -> other as Priority <- rtn as Boolean: (this <=> other) < 0 default operator
Correct:
default operator
E07184 — A hand-written <=> needs a hand-written #? that hashes what <=> compares (here, level). A defaulted #? would hash every field and disagree with the sameness, so it is E07184. See ek9 -h E07184 for details.
Incorrect:
default operator #?
Correct:
operator #? as pure <- rtn as Integer: #?level
Other ways to ask this
- How do <, >, <=, >= work on a record?
- Show me comparison operators on a custom record
- How do I make a record sortable in EK9?
Coming from another language?
Java: implements Comparable. Python: __lt__, __gt__, etc. (functools.total_ordering). Rust: derive Ord. EK9: define <=> (and #?); the compiler generates <, >, <=, >=, == and <> from it.
Keywords: sort, record, spaceship, E07184, operator, comparison, sameness, E07181