Language Reference
The complete semantics behind the Syntax tour.
Core built-ins
Section titled “Core built-ins”No import needed. print writes without a newline, println adds one,
input prints an optional prompt and reads a line (empty string on EOF).
print("name: ")name := input()println("Hello, {name}!")
answer := input("Continue? (y/n) ")Primitive types
Section titled “Primitive types”| Type | Description | Example |
|---|---|---|
int |
64-bit signed integer | 42, -1 |
float |
64-bit IEEE 754 float | 3.14, -0.5 |
bool |
Boolean | true, false |
str |
UTF-8 string | "hello" |
unit |
Empty tuple / void | () |
Compound types
Section titled “Compound types”Homogeneous, dynamically-sized arrays and insertion-ordered dicts:
nums: [int] = [1, 2, 3]names: [str] = ["Ada", "Grace"]ages: {str: int} = {"Ada": 36, "Grace": 85}Structs are named records with typed fields and field access:
struct Point { x: int, y: int }struct Rect { origin: Point, w: int, h: int }
p := Point{ x: 1, y: 2 }r := Rect{ origin: p, w: 10, h: 20 }println(p.x) // 1Fields are mutable through the binding, and structs compose across modules:
p := Point{ x: 1, y: 2 }p.x = 10Variants: Option and Result
Section titled “Variants: Option and Result”Two built-in variant types use dot-prefixed literals:
x := .some(42)y := .nonea := .ok("success")b := .err("failed")int() and float() parse strings into Option (.none on failure):
count := input("How many? ") |> int() ?? 1Inference rules
Section titled “Inference rules”:= infers from the right-hand side; : Type = checks against it:
x := 42 // intpi := 3.14 // floaty: int = 42 // OK// y: int = 3.14 // ERROR: expected int, found floatReturn types are inferred from the body when omitted, and generic parameters are inferred at the call site:
func add(a: int, b: int) { a + b } // -> int, inferred
func identity<T>(x: T) -> T { x }result := identity(42) // T = intGenerics with bounds
Section titled “Generics with bounds”func min<T: Ord>(a: T, b: T) -> T { if a < b { a } else { b }}
println(min(3, 5)) // 3println(min(2.5, 1.5)) // 1.5The standard library builds on this — vec.min_val, math.clamp and
math.min_arr are all generic over ordered types.
Closures
Section titled “Closures”Closures capture their environment:
mult := 3triple := |x: int| x * multprintln(map(range(1, 4), triple)) // [3, 6, 9]Parameter types can be omitted when inference can see through:
zz eval "range(1, 6) |> map(|x| x * 2) |> str"[2, 4, 6, 8, 10]Operator semantics
Section titled “Operator semantics”| Operator | Operands | Result |
|---|---|---|
+ - * / % |
int, int |
int (division truncates: 7 / 2 == 3) |
+ - * / |
float, float |
float |
+ |
int, float |
float |
+ |
str, str |
str (concatenation) |
+ |
[T], [T] |
[T] (concatenation) |
** |
int, int |
int (power) |
== != |
any, same type | bool |
< > <= >= |
numbers | bool |
&& || |
bool |
bool |
?? |
any, T |
T — unwraps Option, passes anything else through |
|> |
a, f |
f(a) — with _ choosing the slot |
Ranges come from range() in one-, two- and three-argument form, including
negative steps:
range(5) // [0, 1, 2, 3, 4]range(10, 15) // [10, 11, 12, 13, 14]range(0, 20, 5) // [0, 5, 10, 15]range(10, 0, -2) // [10, 8, 6, 4, 2]Truthiness
Section titled “Truthiness”Only false and .none are falsy — 0, "" and [] are truthy, so compare
them explicitly:
if count == 0 { println("empty")}Type errors don’t cascade
Section titled “Type errors don’t cascade”When the checker hits a type error it marks the value instead of spraying follow-on errors:
x := undefined_var // ERROR: undefined variable `undefined_var`y := x + 1 // no additional errorSee Error Handling for turning these into great diagnostics.