E07187: '<?' and '>?' select by '<=>', which is unset for two values of different run-time types: they cannot be used on an open or abstract class, record or component, or a trait, unless its sameness is hierarchy-wide - make the type closed, write '<=>' by hand on its abstract base or trait, or compare with '<=>' and handle unset yourself
What went wrong
'<?' and '>?' select one of their two operands by comparing them with '<=>'. That only works when every two set values of the type have an ordering. Here the operands' type is an open class, an abstract class, an open or abstract record or component, or a trait, so its values can be of DIFFERENT run-time types - a Base and a Kid that extends it, say - and two values of different run-time types are never the same or in order: their '<=>' is UNSET. There is then no lesser or greater value to select.
The result cannot be made unset either. An unset value cannot be built for a type of your own: 'T()' may give a SET value, may not exist or be private, the type may be abstract or a trait, and it would run your code. So the operators are refused for such a type at compile time.
The one exception is hierarchy-wide sameness: a '<=>' written by hand on an abstract base or a trait, over what every sub-type provides (an 'area()' for shapes, say). That '<=>' orders every pair of values, whatever their run-time types, so the operators can always choose.
In a generic, the body cannot know what 'T' will be, so the check is made where 'T' is given its type: making a 'Keeper' of Base, or 'lesserOf() of Base', is the error when the generic uses one of these operators on its 'T' and Base is such a type.
Closed classes, closed records and closed components, enumerations, constrained types and the built-in types are never affected.
Worked examples
The [EXAMPLE] section below shows '<?' on an open class, then the fix - the class made closed, so both values are always of one run-time type.
How to fix it
- If nothing needs to extend the type, make it closed: remove 'as open'.
- If the hierarchy should be ordered as a whole, write '<=>' by hand on its abstract base or trait, over what every sub-type provides (with a hand-written '#?' beside it, E07184). Every pair of values then has an order.
- Otherwise compare with '<=>' yourself and decide what to do when the result is unset:
//Unset when first and second are of different run-time types: keep first then comparison <- first <=> second lesser <- comparison? and comparison > 0 <- second : first
- In a generic, parameterise it with a closed type, or with a type whose hierarchy has a hand-written '<=>' on its abstract base or trait.
Common causes
- 'as open' added to a class that nothing extends yet
- An abstract base with 'default operator': a defaulted sameness is per-type, it compares each type with itself only
- A generic min/max helper parameterised with the base of a hierarchy
See also
Examples
Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.
Fails with E07187: '<?' and '>?' select by '<=>', which is unset for two values of different run-time types: they cannot be used on an open or abstract class, record or component, or a trait, unless its sameness is hierarchy-wide - make the type closed, write '<=>' by hand on its abstract base or trait, or compare with '<=>' and handle unset yourself
#!ek9 defines module error.e07187.bad defines class Base as open tag as String: String() Base() -> initial as String tag :=? initial default operator Kid extends Base Kid() -> initial as String super(initial) defines program Lesser() stdout <- Stdout() first as Base: Base("a") second as Base: Kid("b") //A Base and a Kid are different run-time types: their '<=>' is unset, so there is no lesser one lesser <- first <? second stdout.println($lesser)
Fixed: Make the class closed, so both values are always of one run-time type
#!ek9 defines module error.e07187.good defines class //Closed: nothing extends Base, so two Base values always compare Base tag as String: String() Base() -> initial as String tag := initial default operator defines program Lesser() stdout <- Stdout() first <- Base("a") second <- Base("b") lesser <- first <? second stdout.println($lesser)
Fixed: Keep the hierarchy open, compare with '<=>' and decide what an unset comparison means
#!ek9 defines module error.e07187.good.compare defines class Base as open tag as String: String() Base() -> initial as String tag := initial default operator Kid extends Base Kid() -> initial as String super(initial) defines program Lesser() stdout <- Stdout() first as Base: Base("a") second as Base: Kid("b") //Unset when first and second are of different run-time types: keep first then comparison <- first <=> second lesser <- comparison? and comparison > 0 <- second : first stdout.println($lesser)
Questions and answers
These Q&A entries show E07187 and its fix: