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