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
  • break exits 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

CategoryOperators
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.

TypeExample
Primitivesnum, bool, string, unit
Arraysarray<num, 1>, array<num, 2> (rank is part of type)
Records{x: num, y: num} (structural, width subtyping)
Dictionariesdict<num> (string keys, uniform value type)
Functions(num, num) -> num
Unionsnum | 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)

CategoryFunctions
Coreprint, len, sum, range, error
Conversionnum, int, str, bool
Mathabs, min, max, floor, ceil, round, sign, trunc
Searchfind (strings and arrays)
Arrayspush, pop, set, sort, reverse, slice, concat
Objectskeys, values, dict, merge, get
Seeded randomsrand, srandn, srandint
Type checkingtype, 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)

ModuleKey exports
mathsin, cos, tan, sqrt, pow, log, exp, hypot, clamp, lerp, PI, E, TAU
arraylinspace, logspace, arange, zeros, ones, eye, reshape, flatten, unique, argsort, cumsum
stringupper, lower, trim, split, join, contains, replace, replace_all, starts_with, ends_with
randomrand, randn, randint, choice, shuffle, sample
linalgdot, cross, norm, det, solve, inv, eigvals, qr, lu, matmul, transpose
statsmean, median, std, variance, cov, corr, quantile
complexcomplex, polar, conj, abs, arg + arithmetic
interpinterp1, interp2, lookup1d, lookup2d, prelookup
nonlinearsaturate, dead_zone, rate_limiter, backlash, relay, quantize
signalhamming, 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, vec3add, sub, dot, cross, magnitude, normalize, rotate, lerp