E07186: a subtype of an ordered type (its super has '<=>') must stay ordered: it declares '<=>' (by hand or 'default operator <=>'), not '==' alone, and a bare 'default operator' needs every one of its fields to have a '<=>'

Category: E07xxx Full resolution · Reported in phase: FULL_RESOLUTION · Classification: ORDERED_SUPER_NEEDS_ORDERED_SAMENESS · All error codes

What went wrong

This type extends an ORDERED type - a super with '<=>' - and declares its own sameness as '==' only. The type would then answer two questions differently: its own '==' compares all its fields, while the '<=>' it inherits from the super compares only the super's. Two values that '==' says differ would still give '<=>' 0, so a sort, a sorted collection or '<?' would treat them as the same.

A type defines its SAMENESS once and every other comparison is generated from it. Below an ordered type the sub-type's own sameness keeps the type ordered: it declares '<=>' over all its fields, and '==' is generated from that '<=>', so the two can never disagree.

Below an equatable-only super (one with '==' and no '<=>'), this error does not apply: below an equatable-only super you may keep '==', or write '<=>' by hand to order the sub-type. A generated '<=>' there is E07190 - it would order the super's fields first, through a '<=>' the super does not have.

The same happens when the sub-type asks for its sameness with the BARE 'default operator' and one of its fields cannot be ordered - a Dict, a List or any type with no '<=>'. The bare 'default operator' gives the sub-type '<=>' only where every field can be ordered. Here it could not, so the type would be left with '==' alone while its super is ordered: the same disagreement, made silently. The message names the field that cannot be ordered. A sub-type of an ordered type is ordered, or it is not a sub-type.

Worked examples

The [EXAMPLE] section below shows a Named that extends an ordered Holder and asks for 'default operator ==', and a Bag that extends it with a Dict field and the bare 'default operator', then the fixes - 'default operator <=>' in place of '==', and a Bag that holds a Holder.

How to fix it

  1. Declare '<=>' in place of '==': 'default operator <=>' compares the super's '<=>' first and then the type's own fields; '==' and the rest are generated.
  2. Or write '<=>' by hand over the same fields, with a hand-written '#?' beside it (E07184).
  3. If the type should not be ordered at all, it cannot extend an ordered type: hold the other type as a field instead (composition). That is the fix for a bare 'default operator' over a field that cannot be ordered: 'Bag' holds a Holder, it is not one.
  4. For a bare 'default operator' over such a field, you may instead write '<=>' by hand over the fields that can be ordered, with a hand-written '#?' beside it (E07184).

Common causes

  • 'default operator ==' on a sub-type, copied from an equatable-only example
  • A hand-written '==' over a new field in a sub-type of a type with '<=>'
  • A bare 'default operator' on a sub-type of an ordered type that adds a Dict, a List or another field type with no '<=>'

See also

  • E07181: comparisons generated from the sameness
  • E07182: hierarchy-wide sameness (written by hand on an abstract base or trait) is sealed
  • E07184: '#?' goes with the sameness

Examples

Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.

Fails with E07186: a subtype of an ordered type (its super has '<=>') must stay ordered: it declares '<=>' (by hand or 'default operator <=>'), not '==' alone, and a bare 'default operator' needs every one of its fields to have a '<=>'

#!ek9
defines module error.e07186.bad

  defines class

    Holder as open
      count as Integer: 1

      Holder()
        -> count as Integer
        this.count: count

      default operator

    Named extends Holder
      name as String: String()

      Named()
        ->
          count as Integer
          name as String

        super(count)
        this.name: name

      //Holder is ordered: the '<=>' Named inherits compares only count, so it would disagree with this '=='
      default operator ==

      default operator ?

      default operator $

      default operator $$

      default operator #?

      default operator :=:

    //Holder is ordered, and Bag's field 'extras' is a Dict, which has no '<=>': the bare 'default operator' below
    //cannot be given one - the type would be equatable only while Holder is ordered
    Bag extends Holder
      extras as Dict of (String, Integer): Dict() of (String, Integer)

      Bag()
        -> count as Integer
        super(count)

      default operator

Fixed: Declare '<=>', or hold the ordered type instead

#!ek9
defines module error.e07186.good

  defines class

    Holder as open
      count as Integer: 1

      Holder()
        -> count as Integer
        this.count: count

      default operator

    Named extends Holder
      name as String: String()

      Named()
        ->
          count as Integer
          name as String

        super(count)
        this.name: name

      //'<=>' compares Holder's count, then name; '==' and the rest are generated from it
      default operator <=>

      default operator ?

      default operator $

      default operator $$

      default operator #?

      default operator :=:

    //A Bag that is not a Holder: it HOLDS one, and its bare 'default operator' gives it '==' alone
    Bag
      holder as Holder: Holder(1)
      extras as Dict of (String, Integer): Dict() of (String, Integer)

      default operator

Questions and answers

These Q&A entries show E07186 and its fix: