E07182: sameness ('<=>' or '==') written by hand on an abstract base or a trait is hierarchy-wide sameness; a subtype inherits it and cannot redefine it
What went wrong
This type inherits its sameness ('<=>' or '==') from an ABSTRACT base or a TRAIT where it is WRITTEN BY HAND, and declares sameness of its own. A hand-written sameness on an abstract base or a trait is ONE sameness for the whole hierarchy - it is how values of different kinds compare (every Shape ordered by area) - so it is sealed there: every subtype inherits it and cannot change it. That is HIERARCHY-WIDE sameness. Everything else is PER-TYPE sameness, including a DEFAULTED sameness on an abstract base: it compares only the base's own fields and is not sealed, so each subtype may 'default' its own over all its fields, exactly as below any other type.
Why: any subtype refinement of a hierarchy-wide ordering is non-transitive once values of different subtypes are mixed. With Circle ordered by radius and every Shape by area you can get c1 < s, s < c2, yet not c1 < c2 - so a sort depends on the order of its input. Bloch (Effective Java item 10): "there is no way to extend an instantiable class and add a value component while preserving the equals contract". EK9 makes that a rule.
PER-TYPE sameness is that type's own. A subtype of a type with per-type sameness may declare or 'default' its own '<=>' or '==' over all its fields, inherited ones included - and one that adds fields must (E07238) - and values of different run-time types are never the same - '==' is false and '<=>' unset. The compiler checks the run-time type before the comparison runs, so the author only ever writes the same-type comparison.
One sameness per type. A hierarchy-wide '<=>' is the only view the whole hierarchy has: a Card ordered by 'rank()' in its trait has Card(5, "hearts") == Card(5, "spades"), because rank is all the trait compares. To tell suits apart put the suit into the trait's ordering. For other views - by value alone, 'aces high' - write a comparator function and 'sort by' it; a second '<=>' on the type would be a second sameness (see ek9 -q 2280).
EK9 codifies Java's equals/hashCode/compareTo contract (Effective Java items 10, 11 and 14) as a compile error: a type defines its SAMENESS once - '<=>' if it is ordered, '==' if it is equatable only - and every other comparison is generated from it, so two ways of saying "equal" cannot disagree.
Worked examples
Compilable examples demonstrating this error and the correct fix:
- ek9 -q 1402 (How do I define equality and ordering for my own type?)
How to fix it
- Delete the subtype's '<=>' / '==' (and its '#?'): it inherits the hierarchy's.
- A bare 'default operator' on the subtype is fine - it gives only the set-check '?', the String and JSON forms '$' and '$$', and the copy.
- If each type should compare only with its own kind (per-type sameness), let the abstract base 'default' its sameness (or declare none, where it adds no fields below another sameness - E07238) and give each subtype its own ('default operator' or a hand-written '<=>').
- If code needs a different order for one purpose, write a comparator function and 'sort by' it.
Common causes
- 'default operator <=>' on a subclass of an abstract base that orders by '<=>'
- A subclass of a trait with '<=>' narrowing it to '<=>(Sub)'
See also
Examples
Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.
Fails with E07182: sameness ('<=>' or '==') written by hand on an abstract base or a trait is hierarchy-wide sameness; a subtype inherits it and cannot redefine it
#!ek9 defines module error.e07182.bad defines class Shape as abstract area() as pure abstract <- rtn as Float? operator <=> as pure -> other as Shape <- rtn as Integer: area() <=> other.area() operator #? as pure <- rtn as Integer: #?area() Circle extends Shape radius as Float: 0.0 Circle() -> radius as Float this.radius: radius override area() as pure <- rtn as Float: 3.14 * radius * radius default operator <=> default operator ? //Shape orders EVERY shape, of any kind, by area: one ordering for the whole hierarchy
Fixed: Inherit it
#!ek9 defines module error.e07182.good defines class Shape as abstract area() as pure abstract <- rtn as Float? operator <=> as pure -> other as Shape <- rtn as Integer: area() <=> other.area() operator #? as pure <- rtn as Integer: #?area() Circle extends Shape radius as Float: 0.0 Circle() -> radius as Float this.radius: radius override area() as pure <- rtn as Float: 3.14 * radius * radius //Inherits '<=>', the comparisons and '#?' from Shape: a Circle orders against any Shape by area default operator ?
Questions and answers
These Q&A entries show E07182 and its fix:
- Q1402: How do I define equality and ordering for my own type, and why can't I write '==' beside '<=>'?
- Q2280: Why is my Card equal to another Card of the same rank but a different suit, and how do I order cards another way?
- Q2282: What is the difference between hierarchy-wide sameness and per-type sameness, and which one do I have?