Operator Overloading
Define operators as statically typed class methods. Prefix and postfix operations have distinct declarations; their return values and mutations are explicit in your code.
Supported Operators
Instance unary operators have no explicit operand parameter; instance binary operators receive the other operand. Static unary and binary overloads receive one and two operands, respectively. Resolution follows static parameter types, visibility, and the normal method dispatch rules. Redefinition does not change precedence.
| Family | Operators | Declaration / Contract |
|---|---|---|
| Arithmetic | + - * / // % ** @ | For example, operator+(other as Point) as Point. |
| Equality and ordering | == != < <= > >= | Comparison overloads return Boolean. |
| Bitwise | & | ^ << >> | Binary methods receive the other operand. |
| Compound assignment | += -= *= /= //= %= **= @= &= |= ^= <<= >>= | For example, operator+=(other as Point) as Void explicitly mutates the receiver. |
| Unary | + - ~ not | For example, operator~() as Mask; not returns Boolean. |
| Prefix | ++value --value | operator++() / operator--(). |
| Postfix | value++ value-- | operator()++ / operator()--; parameterless instance methods. |
| Indexing | value[index] | operator[] with get and/or set accessors. |
| Membership | item in container | operator in(item as T) as Boolean belongs to the container, on the right. |
| Pattern matching | value =~ pattern | operator=~(pattern as String) as Boolean. |
| Invocation | value(arguments) | operator ()(...) with ordinary typed parameters. |
Declare each supported form explicitly: an equality overload does not automatically supply
every comparison, and a prefix overload does not supply a postfix overload. Compound
assignments such as value += other may have their own overload, whose return
value is discarded. Without a compound overload, the corresponding binary operation and
assignment require an assignable, type-compatible result. Mutating the receiver and
replacing the variable with another instance are different contracts; choose explicitly.
Membership negation, item not in container, negates operator in.
Assignment =, type tests is, casts as, and
sizeof are language operations, not custom operator methods. Native Boolean
and and or short-circuit; a custom lazy overload contract is not
currently supported and must not be inferred from an ordinary binary method declaration.
Declarations and Examples
class Point:
public x as Int
public y as Int
public Point(x as Int, y as Int):
this.x = x
this.y = y
public operator+(other as Point) as Point:
return Point(this.x + other.x, this.y + other.y)
public operator+=(other as Point) as Void:
this.x += other.x
this.y += other.y
public operator==(other as Point) as Boolean:
return this.x == other.x and this.y == other.y
first = Point(1, 2)
second = Point(3, 4)
assert first + second == Point(4, 6)
assert first.x == 1
alias = first
first += second
assert alias == Point(4, 6)
Operators use ordinary syntax at the call site. A type may define only the operators that make sense for its domain.
Unary +, -, ~, and not
also use ordinary method resolution. An instance overload has no explicit
operand parameter; a static overload has one. The operand is evaluated
exactly once, and the usual visibility and virtual dispatch rules apply.
An overload of not must return Boolean.
class Mask:
public bits as Int
public Mask(bits as Int):
this.bits = bits
public operator~() as Mask:
return Mask(~this.bits)
inverted = ~Mask(0)
assert inverted.bits == -1
Prefix and Postfix Increment / Decrement
Increment and decrement have distinct prefix and postfix overloads:
operator++() and operator--() implement
++value and --value;
operator()++ and operator()-- implement
value++ and value--. Postfix declarations
are parameterless instance methods: the empty parentheses precede
the symbol, with no second parameter list. Each form uses ordinary
visibility and virtual dispatch and evaluates its receiver exactly once.
No overload is substituted for a missing form, and no copy is implicit.
The overload's declared return type determines the expression's type.
class Counter:
public value as Int = 0
public operator++() as Counter:
this.value++
return this
public operator()++ as Counter:
previous = Counter()
previous.value = this.value
this.value++
return previous
public operator--() as Counter:
this.value--
return this
public operator()-- as Counter:
previous = Counter()
previous.value = this.value
this.value--
return previous
counter = Counter()
previous = counter++
assert previous.value == 0
assert counter.value == 1
current = ++counter
assert current.value == 2
previous = counter--
assert previous.value == 2
assert counter.value == 1
current = --counter
assert current.value == 0
Returning this preserves identity; constructing another
object preserves a snapshot. Writing previous = this
only creates an alias and does not preserve the previous state.
Boolean and and or short-circuit:
their right operand is evaluated only when the left operand does
not determine the result.
Callable Instances
Define operator () to make a class instance callable.
Its parameters, return type, default arguments, and access checks
follow the same rules as an ordinary method. Calling the instance
does not implicitly call a method named invoke.
The first empty pair () names the operator; the second
pair contains its parameters. For a parameterless call operator,
write operator ()(). This is distinct from the postfix
declarations operator()++ and operator()--.
class Multiplier:
public operator ()(value as Int = 2) as Int:
return value * 3
multiply = Multiplier()
assert multiply(5) == 15
assert multiply() == 6
assert (multiply)(5) == 15
Indexed Access
Declare indexed reads and writes with one operator[] member. The setter receives
the assigned value through value. Declare only get for a read-only
indexer, or only set for a write-only indexer. Do not declare operator[]=.
Multiple typed index parameters are allowed, for example operator[](row as Long, column as Long).
Accessors must be indented one level below the indexer declaration, with their bodies
one level further. Each accessor may be declared at most once, and a concrete indexer
must declare at least one accessor. Reading a write-only indexer or assigning through
a read-only indexer is rejected at compilation with TypeError.
class Slot:
private _stored as Int = 0
public operator[](index as Long) as Int:
get:
if index != 0:
throw IndexException("Only index 0 exists")
return this._stored
set:
if index != 0:
throw IndexException("Only index 0 exists")
this._stored = value
slot = Slot()
slot[0] = 42
assert slot[0] == 42
Membership
class Interval:
public operator in(value as Int) as Boolean:
return 1 <= value <= 3
interval = Interval()
assert 2 in interval
assert 4 not in interval
The receiver is the right operand. This instance method takes exactly one item parameter
and returns Boolean; it is not a parameter-passing qualifier.