Operators
The complete operator set, from calls down to the pipe.
Precedence
| Level | Operators | Notes |
|---|---|---|
| 1 | f(x) s[i] m.field | call, index, member |
| 2 | -x +x ~x | unary minus, plus, bitwise not |
| 3 | ** | power; groups right to left |
| 4 | * / % | multiplication, division, remainder |
| 5 | + - | also text and seq concatenation |
| 6 | .. | range construction |
| 7 | << | shift left |
| 8 | & | bitwise and |
| 9 | ^ | bitwise xor |
| 10 | | | bitwise or |
| 11 | == != < <= > >= | comparisons do not chain |
| 12 | not | logical negation |
| 13 | and | short-circuit |
| 14 | or | short-circuit |
| 15 | ? | filter |
| 16 | >> | pipe |
Arithmetic
print(7 + 2 * 3) # 13
print(2 ** 10) # 1024
print(2 ** 3 ** 2) # 512, ** groups right to left
print(2 ** -1) # 0.5
print(7 / 2) # 3.5, division keeps integers only when exact
print(-7 % 5) # 3, remainder is never negative
Integer arithmetic is checked: overflow raises OverflowError, and
division or remainder by zero raises ZeroDivisionError. The full numeric
rules live in Values and Types.
+ also concatenates. Text plus text joins the strings; seq plus seq
builds a new sequence:
print("j" + "2") # j2
print([1, 2] + [3]) # [1,2,3]
Comparison
The six comparisons work on numbers (integers and floats compare across the divide)
and on text, which orders lexicographically. Comparing other types with
< raises TypeError.
Equality is broader: any two values can be tested with == and
!=. Sequences compare element by element, values of different types are
simply unequal, and NAN equals nothing:
print(1 == 1.0) # true
print([1, 2] == [1, 2]) # true
print("b" > "a") # true
print(1 == "1") # false
print(NAN == NAN) # false
Comparisons do not chain. a < b < c parses as
(a < b) < c, which compares a bool and will usually raise
TypeError; write a < b and b < c.
Logic
and and or short-circuit, and they return one of their
operands rather than forcing a bool. not always returns a bool. Truthiness
is defined in Values and Types.
print(5 and 3) # 3, the right operand
print(0 or 7) # 7, the first truthy operand
print(not 0) # true
# Short-circuiting guards the division from ever running.
z = 0
safe = z != 0 and 10 / z > 1
print(safe) # false
Bitwise
Bitwise operators work on integers: &, |,
^, the unary ~, and left shift <<. For
right shift and bit inspection, use the bits module
(bits.shr, bits.popcount; see the
Standard Library).
flags := 0
flags = flags | (1 << 0) # set bit 0
flags = flags | (1 << 2) # set bit 2
print(flags) # 5
print(flags & (1 << 2) != 0) # true, bit 2 is set
flags = flags & ~(1 << 2) # clear bit 2
print(flags) # 1
print(bits.shr(16, 2)) # 4
Note that ^ is xor, not exponentiation; powers are spelled
**.
Ranges
a..b builds a range that includes both endpoints; 1..5 is
1, 2, 3, 4, 5. Ranges drive loops, convert to sequences with collect, and
feed reductions directly. An open range a.. is an infinite flow, covered in
Sequences, Maps, and Flows.
for i in 1..3 { print(i) } # 1 2 3
print(sum(1..100)) # 5050
print(collect(2..5)) # [2,3,4,5]
The pipe
value >> f applies a function. If the left side is a sequence,
f is applied to every element and the results form a new sequence; if it is
a scalar, the pipe is a plain call. Because the pipe binds loosest, whole expressions
flow into it cleanly.
func sq(x) = x * x
nums = [1, 2, 3, 4]
print(nums >> sq) # [1,4,9,16]
print(9 >> sq) # 81
The filter
seq ? predicate keeps the elements for which the predicate holds. The
predicate is either a one-argument function or an inline expression using
_ as the current element:
nums = [1, 2, 3, 4, 5, 6]
func is_even(n) = n % 2 == 0
print(nums ? is_even) # [2,4,6]
print(nums ? (_ > 3)) # [4,5,6]
Pipe and filter compose into small pipelines that read left to right:
func double(x) = x * 2
nums = [1, 2, 3, 4, 5]
print(nums ? (_ % 2 == 1) >> double) # [2,6,10]