Operators¶
Kiwi provides a full set of operators for arithmetic, comparison, logic, bitwise manipulation, and assignment. Most operators work on the types you'd expect; a few have extra behavior for strings and lists worth knowing about.
Arithmetic¶
| Operator | Name | Example | Result |
|---|---|---|---|
+ |
Add | 3 + 4 |
7 |
- |
Subtract | 10 - 3 |
7 |
* |
Multiply | 3 * 4 |
12 |
/ |
Divide | 10 / 4 |
2.5 |
// |
Integer divide | 7 // 2 |
3 |
% |
Modulo | 10 % 3 |
1 |
** |
Exponent | 2 ** 8 |
256 |
/ always returns a float: Division always produces a float, even when both operands are integers. Use // when you need an integer result.
println 10 / 4 # 2.5 (always float)
println 10 / 4.0 # 2.5
println 10 // 4 # 2 (integer division — use // for whole-number results)
// always returns an integer (floor division): Floors both operands to integers before dividing, and the result always rounds toward negative infinity regardless of sign.
println 7 // 2 # 3
println -7 // 2 # -4 (floors toward -∞, not toward 0)
println 7.9 // 2 # 3 (float operand is floored first)
String and list + and *: + concatenates strings and lists; * repeats them.
println "hello" + " world" # hello world
println [1, 2] + [3, 4] # [1, 2, 3, 4]
println "ha" * 3 # hahaha
println [0] * 4 # [0, 0, 0, 0]
Comparison¶
| Operator | Name | Example |
|---|---|---|
== |
Equal | a == b |
!= |
Not equal | a != b |
< |
Less than | a < b |
<= |
Less than or equal | a <= b |
> |
Greater than | a > b |
>= |
Greater than or equal | a >= b |
Comparison works across integer and float without casting. Strings are compared lexicographically.
Logical¶
Kiwi accepts both symbol and keyword forms — they are identical in behavior.
| Symbol | Keyword | Description |
|---|---|---|
&& |
and |
True when both operands are truthy |
|| |
or |
True when at least one operand is truthy |
! |
not |
Inverts a boolean |
Both forms short-circuit: &&/and stops at the first falsy value; ||/or stops at the first truthy value.
println true && false # false
println true and false # false (same)
println false || true # true
println false or true # true (same)
println !true # false
println not true # false (same)
Null-coalescing (??)¶
Returns the left operand if it is not null, otherwise evaluates and returns the right operand. Chains left-to-right and short-circuits at the first non-null value.
x = null
println x ?? "default" # default
# Chains resolve to the first non-null value
println null ?? null ?? "found" ?? "other" # found
# Useful for optional function results
fn maybe(): any
null
end
result = maybe() ?? "fallback"
println result # fallback
Ternary (? :)¶
Inline conditional expression.
The when postfix guard is a clean alternative for single-expression returns and early exits:
Bitwise¶
| Operator | Name | Example | Notes |
|---|---|---|---|
& |
AND | 5 & 9 → 1 |
Bit-by-bit AND |
| |
OR | 5 | 9 → 13 |
Bit-by-bit OR |
^ |
XOR | 5 ^ 9 → 12 |
Bit-by-bit XOR |
~ |
NOT | ~5 → -6 |
Bitwise complement |
<< |
Left shift | 5 << 1 → 10 |
Shift bits left |
>> |
Right shift | 5 >> 1 → 2 |
Arithmetic right shift |
>>> |
Unsigned right shift | a >>> b |
Logical right shift (no sign fill) |
a = 5 # 0101
b = 9 # 1001
println a & b # 1 (0001)
println a | b # 13 (1101)
println a ^ b # 12 (1100)
println a << 1 # 10 (1010)
println a >> 1 # 2 (0010)
println ~a # -6
XOR swap — swap two variables without a temporary:
Assignment¶
Basic and chained assignment¶
Compound assignment¶
Each compound operator reads the current value, applies the operation, and writes the result back.
| Operator | Equivalent to |
|---|---|
+= |
a = a + b |
-= |
a = a - b |
*= |
a = a * b |
/= |
a = a / b |
//= |
a = a // b |
%= |
a = a % b |
**= |
a = a ** b |
n = 20
n += 5 # 25
n -= 10 # 15
n *= 4 # 60
n /= 3 # 20.0 (float — /= follows / semantics)
n //= 3 # 6
n %= 5 # 1
n **= 3 # 1
Compound assignment also works on list indices and hashmap keys:
lst = [10, 20, 30]
lst[1] += 5 # lst is now [10, 25, 30]
h = { score: 100 }
h["score"] -= 10 # h["score"] is now 90
Unpack assignment¶
Assigns multiple variables at once from a comma-separated list or a list value.
a, b, c = 1, 2, 3
println a # 1
println b # 2
println c # 3
# Unpack a list
coords = [10, 20]
x, y = coords
println x # 10
println y # 20
# Swap without a temp variable
p, q = 7, 13
p, q = q, p
println p # 13
println q # 7
# Destructure a function's return value
fn bounds(): list
[0, 100]
end
lo, hi = bounds()
Logical assignment¶
Short-circuit assignment using the logical operators.
| Operator | Meaning |
|---|---|
&&= |
Assign right side only if left side is truthy |
||= |
Assign right side only if left side is falsy |
x = true
x &&= false # x becomes false (was truthy, so rhs was evaluated)
y = false
y ||= "default" # y becomes "default" (was falsy, so rhs was evaluated)
z = "existing"
z ||= "fallback" # z stays "existing" (was truthy, short-circuits)
Bitwise assignment¶
| Operator | Equivalent to |
|---|---|
&= |
a = a & b |
|= |
a = a | b |
^= |
a = a ^ b |
~= |
a = ~b |
<<= |
a = a << b |
>>= |
a = a >> b |
flags = 0b1010
flags |= 0b0001 # set bit 0 → 0b1011
flags &= 0b1110 # clear bit 0 → 0b1010
flags ^= 0b1111 # toggle all → 0b0101
flags <<= 1 # shift left → 0b1010
flags >>= 2 # shift right → 0b0010
Precedence¶
Operators are evaluated in the following order (highest to lowest). Operators on the same row have equal precedence and associate left-to-right unless noted.
| Precedence | Operators | Notes |
|---|---|---|
| 1 (highest) | ** |
Right-associative |
| 2 | unary -, !, not, ~ |
|
| 3 | *, /, //, % |
|
| 4 | +, - |
|
| 5 | <<, >>, >>> |
|
| 6 | & |
|
| 7 | ^ |
|
| 8 | | |
|
| 9 | ==, !=, <, <=, >, >= |
|
| 10 | &&, and |
|
| 11 | ||, or |
|
| 12 | ?? |
|
| 13 | ? : |
Right-associative |
| 14 (lowest) | =, +=, -=, *=, //=, etc. |
Right-associative |
When in doubt, use parentheses:
# Without parens — may not read as intended
println 2 + 3 * 4 # 14 (not 20)
# With parens — unambiguous
println (2 + 3) * 4 # 20
Multi-line expressions¶
Kiwi treats newlines as whitespace, so expressions can continue freely across lines. Place the operator at the end of a line or the start of the next — both work.
Operator overloading¶
Structs can define custom behavior for binary operators by declaring a method named after the operator symbol. See Operator Overloading for the full reference.
struct Vec2
fn new(x, y)
@x = x
@y = y
end
fn +(other: Vec2): Vec2
Vec2.new(@x + other.x, @y + other.y)
end
fn ==(other: Vec2): boolean
@x == other.x && @y == other.y
end
fn to_string(): string
"(${@x}, ${@y})"
end
end
a = Vec2.new(1, 2)
b = Vec2.new(3, 4)
println a + b # (4, 6)
println a == b # false