Functions
A top-level > name declares a function. Bodies indent under the header. Return a value with <<. One-liners collapse the whole body into >> on the same line. Functions live in the global registry, so any page, pup, or service can call them.
A multi-line function
main.rat
Indent the body under the header. A plain name << introduces a local that is scoped to this function - a helper's locals never leak into or clobber its caller's, even when they share a name. (Writes to a name that already exists as server or page state still reach that state; the local rule is only for new names.) Return with a bare <<. Arity is fixed: pass fewer args and the missing slots are null.
> add[a, b]
[sum] << a + b
<< sum
> server
total: add(4, 7) One-liner with >>
Shorthand for "return expr"
> name >> expr is the entire function on one line. Use it for pure computations with no locals or statements, just the expression. The reactive graph picks up >> definitions the same way derived fields do.
> double[x] >> x * 2
> server
d: double(21) Call from a page
No imports or registration, just call
Once declared, the function is in scope everywhere. Call it inside [...] like any other expression. Functions can call each other freely; the resolution order is per-file then global, with builtins last.
> add[a, b] >> a + b
> page
<p> 4 + 7 = [add(4, 7)] 4 + 7 = 11
Lambdas
Anonymous functions for collection helpers
Some builtins (filter, map, reduce) take a callback. Pass an inline lambda with fn >> expr; the eval engine threads it back through the same dispatch path your named functions use.
> server
evens: filter([1, 2, 3, 4], fn[n] >> n % 2 == 0) Return many values, unpack them
<< a, b returns [a, b]; [x, y] << call() binds both
A return with top-level commas is array sugar: << a, b, c returns [a, b, c]. At the call site, [x, y, z] << call() binds the names positionally - fresh locals, like any bracket binding. A null return leaves every name null (so a (x is null) guard still works); a short array null-fills the missing names and a long one ignores the extras. Keep the named-object form for results that travel; positional unpack is for tight local helpers.
> minmax[xs]
<< first(sort(xs)), last(sort(xs))
> page
[lo, hi] << minmax([4, 1, 9, 2])
<p> low [lo], high [hi] low 1, high 9
See also Guards and loops · Services · Interpolation