Operators
Arithmetic Operators #
| Operator | Description | Example |
|---|---|---|
+ |
Addition | 3 + 2 → 5 |
- |
Subtraction | 10 - 4 → 6 |
* |
Multiplication | 3 * 4 → 12 |
/ |
Division | 10 / 3 → 3 |
% |
Modulo | 10 % 3 → 1 |
** |
Exponentiation | 2 ** 8 → 256 |
Comparison Operators #
| Operator | Description | Example |
|---|---|---|
== |
Equal | x == 5 |
!= |
Not equal | x != 0 |
< |
Less than | x < 10 |
> |
Greater than | x > 0 |
<= |
Less or equal | x <= 100 |
>= |
Greater or equal | x >= 1 |
Logical Operators #
| Operator | Description | Example |
|---|---|---|
&& |
Logical AND | a && b |
|| |
Logical OR | a || b |
! |
Logical NOT | !flag |
Assignment Operators #
| Operator | Description | Example |
|---|---|---|
= |
Assignment | x = 10 |
+= |
Add and assign | x += 5 |
-= |
Subtract and assign | x -= 3 |
*= |
Multiply and assign | x *= 2 |
/= |
Divide and assign | x /= 4 |
%= |
Modulo and assign | x %= 3 |
Pipe Operator |>
The pipe operator passes the left expression as the first argument to the right function. This creates a natural top-to-bottom data flow:
// Without pipe — hard to read (inside-out)
let result = collect(filter(map(list, |x| x * 2), |x| x > 5))
// With pipe — natural flow
let result = list
|> map(|x| x * 2)
|> filter(|x| x > 5)
|> collect()
How It Works #
a |> f(b, c) transforms to f(a, b, c) — the left side becomes the first argument.
" Hello World "
|> string.trim()
|> string.split(" ")
|> Array.map(|s| string.to_upper(s))
|> Array.join(", ")
Pipe with Placeholder _
When the piped value shouldn't be the first argument:
users
|> sort(_, by: .name)
|> group(_, key: |u| u.age)
Tap Operator &.
Executes a side effect without breaking the chain — the original value passes through unchanged. Perfect for debugging and logging:
list
|> sort()
&. print() // prints the sorted list, passes it along
|> filter(|x| x > 0)
&. |x| log(x) // debug log
|> collect()
How It Works #
a &. f() calls f(a) for its side effect, then returns a unchanged.
Optional Chaining ?.
Safely access fields on values that might be nil:
let city = user?.address?.city // nil if any part is nil
Without optional chaining, you'd need nested nil checks:
// Equivalent without ?.
let city = if user != nil {
if user.address != nil {
user.address.city
} else { nil }
} else { nil }
Null Coalesce ??
Provide a default value when the left side is nil:
let name = user?.name ?? "Anonymous"
let port = config?.port ?? 8080
Combining with Optional Chaining #
let city = user?.address?.city ?? "unknown"
Error Propagation ?
Propagates errors up the call stack, similar to Rust:
fn read_config(path: str) -> Result<Config, Error> {
let text = fs.read(path)? // returns early on error
let config = json.parse(text)?
Result.Ok(config)
}
When ? is applied to a Result.Err, the function immediately returns that error. When applied to Result.Ok(value), it unwraps to value.
Spread Operator ...
Spread elements into arrays or structs:
let a = [1, 2, 3]
let b = [0, ...a, 4, 5] // [0, 1, 2, 3, 4, 5]
let base = { name: "Alice" }
let full = { ...base, age: 30 }
Range Operators #
| Operator | Description | Example |
|---|---|---|
.. |
Exclusive range | 0..10 → 0 to 9 |
..= |
Inclusive range | 0..=10 → 0 to 10 |
for i in 0..5 { // 0, 1, 2, 3, 4
print(i)
}
for i in 0..=5 { // 0, 1, 2, 3, 4, 5
print(i)
}
Operator Precedence #
From highest to lowest:
- Unary:
-x,!x - Exponentiation:
** - Multiplicative:
*,/,% - Additive:
+,- - Range:
..,..= - Comparison:
<,>,<=,>= - Equality:
==,!= - Logical AND:
&& - Logical OR:
|| - Null coalesce:
?? - Pipe:
|> - Tap:
&. - Assignment:
=,+=,-=,*=,/=,%=