What does 'default operator' generate?

← Classes and OOP · Ref: Q116

In EK9, the 'default operator' keyword generates standard operators automatically based on a type's fields. It provides equality, comparison, string conversion, hashing, and isSet checking.

WHAT IT GENERATES

The following operators are auto-generated:

  ==   equality (field-by-field comparison)
  <>   inequality (negation of ==)
  <=>  comparison (returns Integer, field-by-field ordering)
  $    string conversion (concatenation of field strings)
  #?   hashcode (combined hash of all fields)
  ?    isSet (true when all fields are set)

HOW IT WORKS

For each field in declaration order, the generated operator compares, concatenates, or hashes. Comparison uses the first field as primary sort, second as secondary, etc.

WHEN TO USE IT

Use 'default operator' when field-by-field behaviour is correct. This covers most value types, records, and simple classes.

WHEN NOT TO USE IT

Override specific operators when you need custom logic: computed equality, selective comparison, custom string formatting, or partial isSet checking.

RECORD VS CLASS DEFAULTS

Both records and classes support 'default operator'. The generated behaviour is identical, but records expose fields publicly while classes keep them private.

See Q96 for custom operators. See Q93 for class basics. See Q97 for records. See Q98 for record operators. See Q99 for enumeration auto-operators. See Q238 for the complete fixed operator set. See Q242 for conversion operators. See Q245 for implementing a complete custom type.

Example

defines module qa.oop.defaultoperator

  defines class

    Colour
      red <- Integer()
      green <- Integer()
      blue <- Integer()

      Colour()
        ->
          red as Integer
          green as Integer
          blue as Integer
        this.red: red
        this.green: green
        this.blue: blue

      default operator

    //Demonstrates individual default operator
    Weight
      grams <- Float()

      Weight()
        -> grams as Float
        this.grams: grams

      default operator ==
      default operator <>
      default operator <=>
      default operator $
      default operator #?
      default operator ?

  defines trait

    Displayable
      display() as pure abstract
        <- rtn as String?

      override operator ? as pure
        <- rtn as Boolean: true

  defines record

    Address
      street <- String()
      city <- String()

      Address()
        ->
          street as String
          city as String
        this.street: street
        this.city: city

      default operator

  defines program

    DefaultOperatorDemo()
      stdout <- Stdout()

      // === EQUALITY (==) and INEQUALITY (<>) ===

      red1 <- Colour(255, 0, 0)
      red2 <- Colour(255, 0, 0)
      blue <- Colour(0, 0, 255)

      stdout.println(`Equal: ${red1 == red2}`)
      stdout.println(`Not equal: ${red1 <> blue}`)

      // === COMPARISON (<=>) ===

      stdout.println(`Compare: ${red1 <=> blue}`)

      // === STRING ($) ===

      stdout.println(`String: ${red1}`)

      // === ISSET (?) ===

      stdout.println(`IsSet: ${red1?}`)

      // === RECORD with default operator ===

      addr1 <- Address("Main St", "Springfield")
      addr2 <- Address("Main St", "Springfield")
      stdout.println(`Address equal: ${addr1 == addr2}`)
      stdout.println(`Address: ${addr1}`)

Common mistakes

E08180 — Class fields must be initialised either with a default value or through constructor assignment. Using 'Integer()' provides a default unset value. Declaring 'red as Integer' without '?' or a value may trigger E08180 if no constructor sets it. See ek9 -h E08180 for details.

Incorrect:

red as Integer

Correct:

red <- Integer()

E50060 — EK9 does not have an 'equals()' method. Use the == operator directly for equality comparison. See ek9 -h E50060 for details.

Incorrect:

stdout.println(red1.equals(red2))

Correct:

stdout.println(`Equal: ${red1 == red2}`)

E50060 — EK9 does not have a compareTo() method. Use the <=> operator directly for comparison. The 'default operator' auto-generates <=> for you. See ek9 -h E50060 for details.

Incorrect:

stdout.println(red1.compareTo(blue))

Correct:

stdout.println(`Compare: ${red1 <=> blue}`)

E50060 — EK9 does not have a toString() method. Use the $ operator or string interpolation. The 'default operator' auto-generates $ for string conversion. See ek9 -h E50060 for details.

Incorrect:

stdout.println(addr1.toString())

Correct:

stdout.println(`Address: ${addr1}`)

E07220 — Not all operators support 'default' auto-generation. Operators like 'contains' have no meaningful field-by-field generation semantics. Only ==, <>, <=>, $, #?, and ? can be auto-generated. See ek9 -h E07220 for details.

Incorrect:

default operator contains

Correct:

default operator ==

E07030 — Traits cannot have 'default operator' because traits define contracts, not data-based implementations. Auto-generation needs concrete fields which traits do not have. Provide explicit operator implementations in traits instead. See ek9 -h E07030 for details.

Incorrect:

default operator

Correct:

override operator ? as pure
        <- rtn as Boolean: true

E07235 — Any aggregate with fields must define operator ? for EK9's tri-state semantics. Without it, guard expressions and safe access patterns cannot inspect field state. Use 'default operator ?' or implement manually. See ek9 -h E07235 for details.

Incorrect:

//operator ? omitted

Correct:

default operator ?
Other ways to ask this
  • Which operators does 'default operator' create automatically?
  • How does auto-generated operator work in EK9?
  • What is the difference between 'default operator' and custom operators?

Coming from another language?

Java: must manually write equals(), hashCode(), toString(), compareTo(). Lombok @Data generates them. Records (Java 16) auto-generate equals/hashCode/toString. Python: @dataclass generates __eq__, __repr__, __hash__. Rust: #[derive(Eq, Hash, Ord, Debug)] generates trait implementations. Go: no auto-generation, must write comparison functions manually. Kotlin: data class generates equals(), hashCode(), toString(), copy(). EK9: 'default operator' generates ==, <>, <=>, $, #?, ? from all fields in declaration order.

Keywords: generate, operator, default, hashcode, string, automatic, field, comparison, object-oriented, equality, isset