Syntax
Variable declarations
Section titled “Variable declarations”The documentation uses explicit declarations:
let x = 5const y = 7x: intx: int = 5let a, b = 1, 2Shorthand declarations are also accepted:
x = 5a, b = 1, 2Those forms create new variables when the names are not already declared in scope.
The docs prefer explicit let because it is easier to scan quickly.
Type annotation
Section titled “Type annotation”x: intdist: [long]adj: [[pair[int, int]]]Function declarations
Section titled “Function declarations”The documentation uses fn:
fn solve(n: int) -> int: n * 2Shorthand function declarations without fn are also accepted:
solve(n: int) -> int: n * 2When a function declares a return type, the last expression in its body is returned
automatically. return is still available for early exits.
Multiple return values
Section titled “Multiple return values”fn minmax(a: [int]) -> (int, int): (a.min(), a.max())Pass by reference
Section titled “Pass by reference”fn add_one(&a: [int]) -> void: for i in [0, len(a)): a[i] += 1Generic functions
Section titled “Generic functions”fn <T> identity(x: T) -> T: xMemoized functions
Section titled “Memoized functions”@memofn fib(n: int) -> long: if n <= 1: return n fib(n-1) + fib(n-2)Default and named arguments
Section titled “Default and named arguments”fn add(a: int, b: int = 1) -> int: a + b
x = add(4)y = add(b = 2, a = 4)Named arguments work for user functions, struct constructors, and helper builtins with optional knobs. The recommended style is to keep the primary data positional and name the option:
st = segtree(a, op = sum)g = grid_graph(mat, dirs = 8)pre = prefix(a, op = max)prod = prefix(a, init = 1, reducer = |acc: int, x: int| acc * x)Structs
Section titled “Structs”struct Point: x: int y: int
p = Point(y = 2, x = 1)Decorators
Section titled “Decorators”@memofn fib(n: int) -> long: if n <= 1: return n fib(n - 1) + fib(n - 2)
@operator(+)fn add(a: Point, b: Point) -> Point: Point(a.x + b.x, a.y + b.y)
@test(input="5\n", expected="25\n")fn square(x: int) -> int: x * xControl flow
Section titled “Control flow”if / elif / else
Section titled “if / elif / else”if x > 0: println "positive"elif x == 0: println "zero"else: println "negative"if as an expression
Section titled “if as an expression”sign = if x > 0 then 1 else if x < 0 then -1 else 0for i in [0, n): # half-open rightfor i in [0, n]: # closedfor i in (0, n): # open bothfor i in [0, n, 2): # explicit stepfor x in a: # iterate collectionfor i, x in a: # index + elementfor (u, v) in edges: # destructuringfor x in a where x > 0: # filterRange notation:
| Syntax | Meaning |
|---|---|
[a, b) | a <= i < b |
[a, b] | a <= i <= b |
(a, b) | a < i < b |
(a, b] | a < i <= b |
Step defaults to +1. If lo > hi with no step specified, the inferred step is -1.
while not q.empty(): # ...while let
Section titled “while let”while let (d, u) = q: # loop ends when q is emptyif let
Section titled “if let”if let (d, u) = pq: println delse: println -1repeat
Section titled “repeat”repeat n: # body runs n timesdefer: vis = filled(n, false)defer runs its body when the current scope exits.
for x in a: guard x > 0 s += x
fn process(x: int) -> int: if x < 0: return -1 x * 2In loops, guard skips to the next iteration. Outside loops, it returns immediately
from the current function or top-level program. Use it only in no-value-return
contexts.
match x: case 0: println "zero" case 1: println "one" default: println "other"
match: case x < 0: println "negative" case x == 0: println "zero" default: println "positive"
sign = match x: case 0: 0 case n where n > 0: 1 else: -1input: n, m: int a: [int][n]
input n: intprintln xprint a, b, cprintln arroutput arroutput arr, sep=" "newlineeach T: println 1Operators
Section titled “Operators”Precedence (high to low)
Section titled “Precedence (high to low)”| Level | Operators | Associativity |
|---|---|---|
| 14 | f(args), a[i], .field, .method(), x++, x-- | left |
| 13 | -x, +x, ~x | right |
| 12 | **, **% | right |
| 11 | *, /, %, //, *% | left |
| 10 | +, -, +%, -% | left |
| 9 | <<, >> | left |
| 8 | ==, !=, <, <=, >, >= | left |
| 7 | & | left |
| 6 | ^ | left |
| 5 | | | left |
| 4 | not | right |
| 3 | and | left |
| 2 | or | left |
| 1 | ? : | right |
Operator semantics
Section titled “Operator semantics”| algo | Meaning |
|---|---|
// | floor division (rounds toward negative infinity) |
% | floor modulo (matches // so that a == (a // b) * b + a % b) |
** | integer exponentiation |
**% | modular exponentiation |
^ | bitwise XOR |
and | logical AND |
or | logical OR |
not | logical NOT |
+% | modular addition |
-% | modular subtraction |
*% | modular multiplication |
x++ | postfix increment |
Assignment operators
Section titled “Assignment operators”=, +=, -=, *=, /=, %=, //=, **=, &=, |=, ^=, <<=, >>=
Chained comparisons
Section titled “Chained comparisons”a < b < c means a < b && b < c. The middle term is evaluated once.
Ternary
Section titled “Ternary”x = a > b ? a : bPipe operator
Section titled “Pipe operator”a |> sort()a |> sort() |> printlng.dijkstra(0).dist[n-1] |> printlnDP block
Section titled “DP block”dp[dimensions] -> type, strategy: base: dp[...] = val <- candidate <- candidate where condition <- candidate for var in range where conditionDimension syntax is var: lo..hi with inclusive bounds. Multiple dimensions are
comma-separated, for example i: 0..n, j: 0..m.
Supported strategies:
maximizeminimizesumanyassign