E07184: '#?' goes with the type's sameness: defaulted with a defaulted one, hand-written with a hand-written one
What went wrong
'#?' does not go with the type's sameness. Equal values must have equal hashes, or Set, Dict and uniq treat them as different:
- hand-written '<=>' or '==' needs a HAND-WRITTEN '#?' over the same fields (a defaulted '#?' hashes every field, so equal values would hash differently);
- defaulted sameness gets its '#?' generated - a hand-written one may disagree;
- a type that inherits its sameness (and declares none of its own) inherits its '#?' too;
- a '#?' on a type with no sameness at all has nothing to agree with;
- where the sameness is declared, '#?' must have a body there - an abstract '#?' beside '<=>' or '==' is an error on every type, a field-less abstract root included: every sub-type of an abstract root inherits the sameness, so none of them may declare the '#?' that abstract one asks for, and no sub-type could be written.
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.
Per-type sameness - a '==' or '<=>' generated or hand-written on a type that is not hierarchy-wide - compares objects of the same run-time type: between two DIFFERENT run-time types (a Leaf and a Middle) '==' is false and '<=>' is unset. Declare the sameness by hand on an ABSTRACT root or a trait when objects of different classes must compare (hierarchy-wide sameness, E07182).
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
- Hand-written sameness: write '#?' over exactly the fields it compares (in the [EXAMPLE] section below, an Account compared by 'id' hashes 'id' alone)
- Defaulted sameness: remove the hand-written '#?'.
- A hash with no sameness: give the type '==' (hashable but not sortable is an equatable-only type), or remove the '#?'.
- An abstract '#?' beside the sameness: give it a body over what the sameness compares (an abstract root writes it over its own abstract accessors), or generate both - the '#?' with a generated '<=>' (or '==').
Common causes
- 'default operator #?' beside a hand-written '<=>'
- A '#?' written only so the type can be used with uniq
- 'operator #? as pure abstract' on the abstract root that declares '<=>'
See also
Examples
Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.
Fails with E07184: '#?' goes with the type's sameness: defaulted with a defaulted one, hand-written with a hand-written one
#!ek9 defines module error.e07184.bad defines trait //A trait with a concrete '==' and an abstract '#?'. Coded code() as pure abstract <- rtn as String? operator == as pure -> other as Coded <- rtn as Boolean: code() == other.code() operator #? as pure <- rtn as Integer? defines class //A field-less abstract root: hand-written '<=>' over an abstract accessor, abstract '#?'. Shape as abstract area() as pure abstract <- rtn as Float? operator <=> as pure -> other as Shape <- rtn as Integer: area() <=> other.area() default operator ? operator #? as pure abstract <- rtn as Integer? //A field-less abstract root with DEFAULTED sameness and an abstract '#?'. Marker as abstract default operator <=> default operator ? operator #? as pure abstract <- rtn as Integer?
Fixed: Hash what it compares
#!ek9 defines module error.e07184.good defines class Account id as Integer? balance as Integer? default private Account() Account() -> identifier as Integer amount as Integer this.id: identifier this.balance: amount identity() as pure <- rtn as Integer: id //Hand-written sameness... operator <=> as pure -> other as Account <- rtn as Integer: id <=> other.identity() //...goes with a hand-written '#?' that hashes what it compares override operator ? as pure <- rtn as Boolean: id? and balance? operator #? as pure <- rtn as Integer: #?id
Questions and answers
These Q&A entries show E07184 and its fix: