Operators

The complete operator set, from calls down to the pipe.

Precedence

LevelOperatorsNotes
1f(x)   s[i]   m.fieldcall, index, member
2-x   +x   ~xunary 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
12notlogical negation
13andshort-circuit
14orshort-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]