What operators does EK9 support and why is the set fixed?

← Operators and Expressions · Ref: Q238

EK9 supports approximately 50 operators in a FIXED set. Unlike C++ where you can define arbitrary operator symbols, EK9 only allows you to IMPLEMENT existing operators on your types. You cannot invent new operator symbols. This is a deliberate design choice that keeps the language predictable and enables comprehensive compile-time validation.

FIXED SET PHILOSOPHY

Every operator in EK9 has defined semantics: purity requirements, parameter counts, and return types. The compiler enforces ALL of these. If you get any wrong, you get a specific error code telling you exactly what is incorrect.

PURITY ENFORCEMENT

Some operators MUST be pure (no side effects): ==, <>, <=>, <, >, <=, >=, $, #?, ?, +, -, *, /, ~, !, abs, sqrt, mod, rem, and, or, xor, contains, matches, #^, #<, #>, length, empty, close.
Some operators CANNOT be pure: +=, -=, *=, /=, ++, --, :=:, :~:, :^:, |.
Violating purity rules produces errors E07500 (must be pure) or E07510 (cannot be pure).

PARAMETER COUNT ENFORCEMENT

Each operator has a fixed parameter count. Binary operators (==, +, -, *, /, and, or) take exactly 1 argument. Unary operators (~, !, ?, $, #?, abs, sqrt, ++, --) take 0 arguments. Incorrect counts produce E06280 (too many parameters) or E06290 (too few parameters).

RETURN TYPE ENFORCEMENT

Return types are strictly enforced: ? must return Boolean, #? must return Integer, $ must return String, and $$ must return JSON. Comparison operators (==, <>, <, >, <=, >=) must return Boolean. The <=> operator must return Integer. Mutation operators (+=, -=, ++, --, :=:, :~:, :^:) must NOT return anything. Violations produce E07520, E07550, E07570, E07580, E07410, E07420, or E07430.

OPERATOR CATEGORIES

Comparison: ==, <>, <, >, <=, >=, <=>, <~> (pure, 1 arg, return Boolean or Integer)
Arithmetic: +, -, *, /, ^, mod, rem (pure, 1 arg, return value)
Unary: - (negate), ~ (NOT for Boolean/Bits), !, abs, sqrt (pure, 0 args, return value)
Mutation: +=, -=, *=, /=, ++, --, :=:, :~:, :^:, | (not pure, no return)
Conversion: $, $$, #?, #^, #<, #> (pure, 0 args, return value)
State: ?, empty, length, close (pure, 0 args)
Logical: and, or, xor (pure, 1 arg, return Boolean)
Bit shift: <<, >> (pure, 1 arg, return value)
Coalescing: ??, ?:, <?, <=?, >?, >=? (expression-level, not implementable)
Query: contains, matches (pure, 1 arg, return Boolean)

See Q96 for class operator syntax. See Q116 for default operator generation. See Q239 for comparison operators. See Q240 for arithmetic operators. See Q241 for mutation operators. See Q242 for conversion operators. See Q243 for coalescing operators. See Q244 for boolean and bitwise operators. See Q245 for implementing a complete custom type. See Q248 for error code ranges and lookup. See Q254 for the Any type and its default operators inherited by all types. See Q279 for common AI operator confusion.

Example

defines module qa.operators.finiteset

  defines class

    Score
      points <- Integer()

      Score() as pure
        -> points as Integer
        this.points :=: points

      operator == as pure
        -> other as Score
        <- rtn as Boolean: points == other.points

      operator <> as pure
        -> other as Score
        <- rtn as Boolean: points <> other.points

      operator <=> as pure
        -> other as Score
        <- rtn as Integer: points <=> other.points

      operator + as pure
        -> other as Score
        <- rtn as Score: Score(points + other.points)

      operator $ as pure
        <- rtn as String: $points

      operator #? as pure
        <- rtn as Integer: #?points

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

  defines program

    FiniteOperatorSetDemo()
      stdout <- Stdout()

      a <- Score(10)
      b <- Score(20)

      // Comparison operators
      stdout.println(`Equal: ${a == b}`)
      stdout.println(`Compare: ${a <=> b}`)

      // Arithmetic operator
      total <- a + b
      stdout.println(`Total: ${total}`)

      // Conversion operators
      stdout.println(`String: ${a}`)

Common mistakes

E07500 — Equality operators must be marked 'as pure' to guarantee no side effects. Omitting 'as pure' on ==, <>, or <=> triggers E07500. See ek9 -h E07500 for details.

Incorrect:

operator ==

Correct:

operator == as pure

E07520 — The == operator must return Boolean. Returning a non-Boolean type like String triggers E07520. See ek9 -h E07520 for details.

Incorrect:

<- rtn as String: points == other.points

Correct:

<- rtn as Boolean: points == other.points

E07550 — The <=> operator must return Integer for three-way ordering. Returning Boolean instead triggers E07550. See ek9 -h E07550 for details.

Incorrect:

<- rtn as Boolean: points <=> other.points

Correct:

<- rtn as Integer: points <=> other.points

E50060 — EK9 types do not have an equals() method. Use the == operator for equality comparisons. Calling a non-existent method triggers E50060 — method not resolved. See ek9 -h E50060 for details.

Incorrect:

stdout.println(`Equal: ${a.equals(b)}`)

Correct:

stdout.println(`Equal: ${a == b}`)
Other ways to ask this
  • Can I define new operator symbols in EK9?
  • What is the complete list of EK9 operators?
  • How does EK9 enforce operator rules at compile time?

Coming from another language?

C++: arbitrary operator overloading including new symbols via operator keyword, no purity or return type enforcement, error-prone. Java: no operator overloading at all, everything is methods. Python: magic methods (__add__, __eq__) with no compiler enforcement of signatures. Rust: trait-based (Add, Eq, Ord) with strict signatures but verbose. Go: no operator overloading. Kotlin: operator keyword with some constraints but less strict than EK9. JavaScript: no custom operators, Symbol-based limited overloading. EK9: fixed set of ~50 operators, compiler enforces purity, parameter count, and return type for every operator.

Keywords: expression, fixed, return, operator, purity, overload, compile, enforce, type, pure, set, migrate, parameter, error, side-effect, immutable, mutation