Quick Reference
A dense lookup for Stone syntax and semantics. For a gentler introduction, start with Overview.
Indexed Mapping
An index variable on the left side triggers automatic iteration. The range is inferred from indexed expressions on the right.
xs = [1, 2, 3, 4, 5]
doubled[i] = xs[i] * 2 // [2, 4, 6, 8, 10]
The index must appear on the right so the compiler can infer bounds:
doubled[i] = xs[i] * 2 // OK: i indexes xs
result[i] = 42 // ERROR: i doesn't index anything
Multiple arrays sharing the same index must have equal lengths. Constants participate in every iteration:
sums[i] = xs[i] + ys[i] // element-wise, same length
scaled[i] = xs[i] * factor // factor is scalar
Multi-Dimensional
doubled[i][j] = matrix[i][j] * 2 // element-wise
grid[i][j] = x[i] + y[j] // cartesian product
transposed[j][i] = matrix[i][j] // transpose by swapping indices
Functions and Field Access in Mappings
squares[i] = square(xs[i])
xs[i] = points[i].x // extract field from array of records
Chaining and Mapping vs Evolve
doubled[i] = xs[i] * 2
plus_one[i] = doubled[i] + 1 // chain: result feeds next mapping
// Mapping: each element independent (parallel)
doubled[i] = xs[i] * 2
// Evolve: accumulation across elements (sequential)
total = evolve (total = 0) for (x in xs) { total' = total + x }
Evolve Loops
Declare state variables upfront. Prime notation (v') sets the next iteration's value.
sum = evolve (sum = 0) for (x in [10, 20, 30]) {
sum' = sum + x
}
// Iteration 1: sum=0, x=10, sum'=10
// Iteration 2: sum=10, x=20, sum'=30
// Iteration 3: sum=30, x=30, sum'=60
// Result: 60
Single State vs Multi State
// Single state — () — returns value directly
total = evolve (sum = 0) for (x in xs) { sum' = sum + x }
// Multi state — {} — returns a record
result = evolve {sum = 0, count = 0} for (x in xs) {
sum' = sum + x
count' = count + 1
}
// result.sum, result.count
// Extract with :=
sum, count := evolve {sum = 0, count = 0} for (x in xs) {
sum' = sum + x
count' = count + 1
}
Loop Drivers
evolve (total = 0) for (x in items) { total' = total + x } // for
evolve (x = 1) while (x < 100) { x' = x * 2 } // while
evolve (x = 1) while (true) { x' = x * 2; if (x' > 1000) { break } } // while true (must break)
State Rules
- If
v'is not assigned, state carries forward unchanged - Assigning
v'more than once in the same iteration is an error - You can read
v'after it has been assigned within the same iteration - All RHS expressions evaluate against current state before transitions
breakexits the loop returning current staged values
// Conditional update — carries forward when false
positive_sum = evolve (positive_sum = 0) for (x in xs) {
if (x > 0) { positive_sum' = positive_sum + x }
}
// Fibonacci — b' = a + b uses CURRENT a, not a'
a, b := evolve {a = 0, b = 1} while (b < 1000) {
a' = b
b' = a + b
}
Common Anti-Patterns
// WRONG: No list comprehensions
result = [x * 2 for x in xs]
// CORRECT:
result[i] = xs[i] * 2
// WRONG: No trailing for clause
result[i] = xs[i] * 2 for (i in range(len(xs)))
// CORRECT:
result[i] = xs[i] * 2
// WRONG: No reassignment
x = 10
x = x + 1
// CORRECT:
x = 10
y = x + 1
// WRONG: No array mutation
arr[0] = 99
// CORRECT:
updated = set(arr, 0, 99)
// WRONG: Don't shadow builtins
fn len(v) = sqrt(v[0]^2 + v[1]^2)
// CORRECT:
fn magnitude(v) = sqrt(v[0]^2 + v[1]^2)
// WRONG: Missing parentheses in for/while
evolve (sum = 0) for x in xs { sum' = sum + x }
// CORRECT:
evolve (sum = 0) for (x in xs) { sum' = sum + x }
Functions
// Expression function
fn add(a, b) = a + b
// Block function — last expression is return value
fn distance(a, b) {
dx = a.x - b.x
dy = a.y - b.y
sqrt(dx * dx + dy * dy)
}
// Early exit: return(expr) with parentheses
fn safe_divide(a, b) {
if (b == 0) { return(0) }
a / b
}
// Default parameters
fn greet(name, greeting = "Hello") = greeting + ", " + name
// Keyword arguments at call site
greet(name = "Alice") // skip to named
greet("Alice", greeting = "Hi") // positional then named
bdf(f, y0, t, tol = 1e-9) // skip defaults, name what you need
// Type annotations (optional)
fn add(a: num, b: num) -> num = a + b
// Functions are first-class values
fn apply(f, x) = f(x)
result = apply(square, 5)
Parameters cannot be rebound inside the function body.
Conditionals
sign = if (x > 0) { "positive" }
elif (x < 0) { "negative" }
else { "zero" }
When used for value, all branches must produce a value and else is required. When used for side effects, else is optional.
Operators
| Category | Operators |
|---|---|
| Scalar arithmetic | +, -, *, /, %, ^ |
| Element-wise array | .+, .-, .*, ./, .^ |
| Matrix multiply | @ |
| Comparison | >, <, >=, <=, ==, != |
| Logical | &&, ||, ! |
| Pipe | |> (passes left as first arg to right) |
Precedence (high to low): member/index, unary, power, multiplicative, @, additive, comparison, equality, &&, ||, |>
Types
Stone is statically typed with full Hindley-Milner type resolution. Annotations are optional.
| Type | Example |
|---|---|
| Primitives | num, bool, string, unit |
| Arrays | array<num, 1>, array<num, 2> (rank is part of type) |
| Records | {x: num, y: num} (structural, width subtyping) |
| Dictionaries | dict<num> (string keys, uniform value type) |
| Functions | (num, num) -> num |
| Unions | num | string |
| Literal types | "on" | "off" |
Schemas
User = { id: num, name: string, role: string = "guest" }
admin: User = {id = 1, name = "Alice", role = "admin"}
guest: User = {id = 2, name = "Bob"} // role defaults to "guest"
// Enum schemas (named unions)
Circle = {kind: string = "circle", radius: num}
Rect = {kind: string = "rect", width: num, height: num}
Shape = Circle | Rect
Empty arrays need annotation: items: array<num, 1> = []
Modules
import zeros, eye, solve from linalg // named imports
import linalg // namespace import
A = linalg.zeros(3, 3)
export fn myFunction(x) = x * 2 // export
export constant = 42
from is a reserved keyword.
Built-in Functions (No Import)
| Category | Functions |
|---|---|
| Core | print, len, sum, range, error |
| Conversion | num, int, str, bool |
| Math | abs, min, max, floor, ceil, round, sign, trunc |
| Search | find (strings and arrays) |
| Arrays | push, pop, set, sort, reverse, slice, concat |
| Objects | keys, values, dict, merge, get |
| Seeded random | srand, srandn, srandint |
| Type checking | type, is_array |
push/pop return new values (no mutation). dict(keys, values) constructs from parallel arrays. merge(a, b) combines records (b overrides). get(obj, key) returns {ok, value}.
Records/dictionaries use dot access. Bracket access is only for arrays.
Standard Library (Require Import)
| Module | Key exports |
|---|---|
math | sin, cos, tan, sqrt, pow, log, exp, hypot, clamp, lerp, PI, E, TAU |
array | linspace, logspace, arange, zeros, ones, eye, reshape, flatten, unique, argsort, cumsum |
string | upper, lower, trim, split, join, contains, replace, replace_all, starts_with, ends_with |
random | rand, randn, randint, choice, shuffle, sample |
linalg | dot, cross, norm, det, solve, inv, eigvals, qr, lu, matmul, transpose |
stats | mean, median, std, variance, cov, corr, quantile |
complex | complex, polar, conj, abs, arg + arithmetic |
interp | interp1, interp2, lookup1d, lookup2d, prelookup |
nonlinear | saturate, dead_zone, rate_limiter, backlash, relay, quantize |
signal | hamming, hanning, filter, conv, xcorr, moving_average, rising_edge, find_peaks |
dynamics/* | fft/ifft, euler/rk4/rk45, tf/ss/zpk, step/impulse/bode, pid/lqr, c_to_d/d_to_c |
vec2, vec3 | add, sub, dot, cross, magnitude, normalize, rotate, lerp |