Operators
Operators
Operators are not built into the numeric types alone. An operator applied to an object routes to a trait method, so any class can support it by implementing the matching trait. An operator applied to a raw FFI scalar lowers directly to a machine instruction.
Operator to trait
| Operator | Trait | Method |
|---|---|---|
+ |
Plus<T> |
plus |
- |
Minus<T> |
minus |
* |
Multiply<T> |
multiply |
/ |
Divide<T> |
divide |
** |
Exp<T> |
exp |
% |
Mod<T> |
modulo |
== |
Equals<T> |
equals |
!= |
NotEquals<T> |
not_equals |
> |
GreaterThan<T> |
greater_than |
< |
LessThan<T> |
less_than |
>= |
GreaterThanEquals<T> |
greater_than_equals |
<= |
LessThanEquals<T> |
less_than_equals |
&& |
And<T> |
and |
| `\ | \ | ` |
! |
Not |
not |
Two more traits back the indexing and iteration syntax:
| Syntax | Trait | Method |
|---|---|---|
a[i] |
Index<I, R> |
index |
a[i] = v |
IndexRef<I, R> |
index_ref |
for x in a |
Iter<T> |
the iterator protocol |
Hash backs use as a Map key, paired with Equals.
Dispatch is by method name
The compiler looks for a method with the right name and signature, not for a
declared impl. A class that defines equals(other: X) => bool supports ==
whether or not it declares impl Equals<X>.
The declaration still matters for bounds. A type that only has the method does
not satisfy impl Equals, so it cannot be used where that bound is required,
such as a Map key. Declare the trait when the type should work generically.
&& and || between two booleans short-circuit on the raw value and never call
And or Or.
Implementing an operator
A class opts in by implementing the trait:
[public] class Vector impl Plus<Vector> {
x: number
y: number
[public] fn plus(other: Vector) => Vector {
return new Vector(this.x + other.x, this.y + other.y)
}
}With that in place, a + b on two Vector values calls plus.