How does purity apply to operators and their overrides?

← Purity Contracts · Ref: Q568

Operators follow the same purity rules as methods. If a parent defines a pure operator, the child override must also be pure (E05150). Many built-in operators are already pure.

PURE OPERATORS

Comparison operators (<=>), equality (==, <>), hash (#?), and string ($) are typically pure. They compute a result without modifying state.

OVERRIDE RULE

If a parent operator is pure, the child override must also be pure:

  override operator <=> as pure
    -> other as MyType
    <- rtn as Integer: ...

OPERATOR ? IS SPECIAL

The operator ? (isSet check) is inherited from the base type. Custom classes use 'override operator ?' and it is typically pure:

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

MUTATING OPERATORS

Operators like +=, -=, :=:, :~:, :^: are mutating by nature and should NOT be pure. These modify the object in place.

See Q560 for pure method basics. See Q561 for purity in overrides. See Q245 for custom type operators. See Q241 for mutation operators.

Example

defines module qa.purity.operators

  defines class

    Temperature as open
      degrees <- 0.0

      Temperature()
        -> degrees as Float
        this.degrees: degrees

      degrees() as pure
        <- rtn as Float: degrees

      //Pure comparison operator
      operator <=> as pure
        -> other as Temperature
        <- rtn as Integer: degrees <=> other.degrees

      //Pure equality derived from comparison
      default operator ==

      //Pure string representation
      operator $ as pure
        <- rtn as String: $degrees + " degrees"

      //Pure hash code
      operator #? as pure
        <- rtn as Integer: #? degrees

      //Pure isSet check
      override operator ? as pure
        <- rtn as Boolean: degrees?

    //Child must keep operators pure
    Celsius extends Temperature
      Celsius()
        -> degrees as Float
        super(degrees)

      //Override of pure operator must be pure
      override operator $ as pure
        <- rtn as String: `${degrees()} C`

      default operator ?

  defines program

    PureOperatorsDemo()
      stdout <- Stdout()

      temp1 <- Temperature(20.0)
      temp2 <- Temperature(30.0)
      stdout.println(`${temp1} vs ${temp2}`)
      stdout.println(`Equal: ${temp1 == temp2}`)
      stdout.println(`Compare: ${temp1 <=> temp2}`)

      cel <- Celsius(100.0)
      stdout.println(`Celsius: ${cel}`)

Common mistakes

E07500 — The parent Temperature.operator $ is pure. The child Celsius override must also be pure. Removing purity from the operator override violates the contract. See ek9 -h E07500 for details.

Incorrect:

override operator $

Correct:

override operator $ as pure

E05120 — When overriding a parent operator, the override keyword is required. Omitting it on operator $ in Celsius triggers shadowing detection. See ek9 -h E05120 for details.

Incorrect:

operator $ as pure

Correct:

override operator $ as pure
Other ways to ask this
  • Can operators be pure in EK9?
  • Must I override a pure operator with a pure operator?
  • How does purity interact with operator overriding?

Coming from another language?

Java: operator overloading not supported, no purity concept. Kotlin: operator overloading exists but no purity enforcement. Rust: trait-based operators, no purity markers. EK9: operators follow identical purity rules to methods, E05150 applies to operator overrides.

Keywords: open, equality, isSet, purity, override, operator, abstract, immutable, pure, contract, mutation, E05150, comparison, side-effect, virtual