Operators

Arithmetic operators

All arithmetic operators work on Int operands and return Int. Mixing types is a TypeError.

OperatorDescriptionExampleResult
+Addition3 + 47
-Subtraction10 - 37
*Multiplication3 * 412
/Integer division7 / 23
%Modulo7 % 21

Integer division truncates toward zero. Division or modulo by the literal 0 is a compile-time TypeError. Division by a runtime zero value causes undefined behaviour.

Operator precedence follows standard mathematical convention:

  • *, /, % bind tighter than +, -
  • Left-to-right associativity within the same precedence level
print((1 + 2 * 3 + 4 * 5).Str())      // 27  (= 1 + 6 + 20)
print((1 + 2 * 3 - 4 * 5 / 2).Str())  // -3

Unary operators

OperatorDescriptionExampleResult
-Arithmetic negation-5-5
!Logical NOT!truefalse

Unary - applies only to Int. ! applies only to Bool. Applying ! to an Int is a TypeError.

Comparison operators

Return Bool. For Int, all six are built in. Str supports == and != only.

OperatorDescription
==Equal
!=Not equal
<Less than
>Greater than
<=Less than or equal
>=Greater than or equal

Comparison operators are not chainable1 < 2 < 3 is a TypeError because the result of 1 < 2 is Bool, and Bool < Int is not defined.

Logical operators

OperatorDescriptionOperand typesReturn type
&&Logical ANDBool, BoolBool
\|\|Logical ORBool, BoolBool
!Logical NOTBoolBool

&& and || do not short-circuit — both sides are always evaluated.

true && false    // false
true || false    // true
!true            // false
0 && 1           // TypeError: && requires Bool operands

String concatenation

+ on two Str values returns a new Str:

"Hello, " + "world!"    // "Hello, world!"
"" + "abc"              // "abc"

None-coalescing operator (??)

Unwraps an Option<T>. Returns the contained value if some, or the right-hand default if none:

def main() {
    let x: Int? = Option.none!<Int>()
    let v = x ?? 42     // v = 42

    let y: Int? = Option.some!<Int>(7)
    let w = y ?? 42     // w = 7
}

Compound assignment

+=, -=, *=, /=, %= — see Variables. These are not overloadable.

Full precedence table (high to low)

LevelOperatorsAssociativity
1unary -, !, typeofright
2*, /, %left
3+, -left
4<, >, <=, >=left
5==, !=left
6&&left
7\|\|left
8??left
9=, +=, -=, *=, /=, %=right

Operator overloading

Classes may define methods named after binary operator symbols. Only binary operators may be overloaded; unary operators (!) may not.

Valid overloadable operators: +, -, *, /, %, ==, !=, <, >, <=, >=, &&, ||, ??.

Rules:

  • The method must take exactly one parameter in addition to self.
  • That parameter must not have a default value.
  • Operator methods can only be defined inside a class, not at the top level.
  • Compound assignment operators (+= etc.) cannot be overloaded.
class Vec2 {
    x: Int,
    y: Int,
    def + (self, other: Vec2) Vec2 ->
        new Vec2 { x: self.x + other.x, y: self.y + other.y },
    def == (self, other: Vec2) Bool ->
        self.x == other.x && self.y == other.y
}

def main() {
    let a = new Vec2 { x: 1, y: 2 }
    let b = new Vec2 { x: 3, y: 4 }
    let c = a + b
    print(c.x.Str())          // 4
    print(c.y.Str())          // 6
    print((a == b).Str())     // false
}

Invalid overload forms:

class C {
    def ! (self) C { ... }              // SyntaxError: ! is not overloadable
    def += (self, other: C) C { ... }   // SyntaxError: compound assignment not overloadable
    def + (self) C { ... }              // TypeError: must take exactly one non-self parameter
}
def + (a: C, b: C) C { ... }           // SyntaxError: top-level operator definition