Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Operator Precedence

Operators listed from lowest precedence (evaluated last) to highest precedence (evaluated first). Operators at the same precedence level are evaluated according to their associativity.

Precedence Table

LevelOperatorDescriptionAssociativity
1||Logical ORLeft
2&&Logical ANDLeft
3== !=EqualityLeft
4< > <= >=ComparisonLeft
5+ -Addition, subtractionLeft
6* / %Multiply, divide, moduloLeft
7! - (unary)Logical NOT, negationRight (unary)
8?Postfix try (Result)Left
9. [] ()Access, index, callLeft

Special Operators

These operators do not fit neatly into the arithmetic precedence chain.

Pipe Operator |>

let result = data |> transform |> validate

The pipe operator has lower precedence than function calls but higher than assignment. It passes the left-hand value as the first argument to the right-hand function.

Pipe Right >>

from users >> keep where active >> sort by name >> take 5

Used in query-style pipe chains. Evaluated left to right.

Spread ...

let merged = [...arr1, ...arr2]
let combined = { ...obj1, ...obj2 }

Prefix operator used inside array and object literals. Not a general expression operator.

Range ..

let r = 1..10

Creates a range value. Used primarily in for loops and slice operations.

Arrow ->

match x {
    1 -> "one",
    _ -> "other",
}

Used in match arms and when arms to separate pattern from result. Not a general operator.

Fat Arrow =>

let f = (x) => x * 2

Lambda shorthand syntax. Separates parameters from body.

Compound Assignment

OperatorEquivalent
+=x = x + value
-=x = x - value
*=x = x * value
/=x = x / value

Compound assignment operators have the same precedence as regular assignment (=). They are statement-level constructs, not expressions.

Type Operators

OperatorContextDescription
:let x: Int = 5Type annotation
?fn f(x: Int?) { }Optional type modifier
<>Array<Int>Generic type parameters

Type operators appear only in type annotation positions and do not participate in expression evaluation.

Examples

// Precedence determines evaluation order
let x = 2 + 3 * 4        // 14 (not 20)
let y = !true || false    // false (! binds tighter than ||)
let z = 1 < 2 && 3 > 1   // true (&& binds looser than < and >)

// Postfix try with field access
let name = get_user()?.name  // ? applies to get_user(), then .name

// Pipe with arithmetic
let result = 5 + 3 |> double  // double(8), not 5 + double(3)

Gotchas

  • Unary - binds tighter than binary operators: -2 * 3 is (-2) * 3 = -6, not -(2 * 3) = -6 (same result in this case, but matters for method calls).
  • The ? operator binds tighter than ., so expr?.field works as expected: it tries expr, then accesses .field on the result.
  • There is no ternary ? : operator. Use if/else expressions or when guards instead.
  • == and != compare by value for all types. There is no identity comparison operator.