Reference

Functions

Generated from LANGUAGE.md at bf2cba0, 2026-08-22.

Definition

To <function name> with a <type> called <param1> and a <type> called <param2>. Return a <type>, <expression>.

No-parameter functions are also valid:

To 'show version'.
  Print "1.0.0".

To ping.
  Print "pong".

Examples:

To 'add numbers' with a number called x and a number called y. Return a number, the x add y.

To 'check divisibility' of a number called divisor and a number called dividend. Return a boolean, the divisor modulo the dividend is 0.

Rules:

  • Function name is a bare single word (add) or a single-quoted multi-word name ('add numbers')
  • Parameters are optional. If present, introduce them with with or of (both work identically)
  • Parameters use a <type> called <name> syntax (bare if single-word, 'single-quoted' if it contains spaces)
  • Multiple parameters joined with and
  • Return type follows Return a <type>,

Function Scope

  • Variables declared at top level are global and can be used inside functions — including inside a function written above the declaration, because the body runs when it is called, and it reads the global as its declared type either way. A function that runs before the declaration has been reached reads the type's empty value — "", [], {}, an empty buffer, 0, 0.0, false — never the value the declaration will go on to store. Top-level code has no such licence: see Declaration Order.
  • Variables declared inside a function are local to that function and are not available at top level.
  • Referencing an unknown variable inside a function is a compile-time error.
  • Assigning to a top-level variable inside a function (Set g to ... / the g is ...) mutates the global itself, so the new value is visible after the call returns and to every other function.
  • Declaring a variable inside a function shadows a top-level variable of the same name (a number called g is 5. inside a function creates a local g); the global is left untouched. Recursion still gets a fresh set of locals per call. This applies to value too: its payload and runtime type tag are stored as a pair, in whichever storage (function-local, or the top-level global's own pair of storage locations) that particular value uses, so a mutation inside one function is never visible to another unless it is genuinely the same global.

Parameter and Local Types

Parameters may use any of the 11 expressible types — number, float, text, boolean, list, map, buffer, file, time, timer, value — and a typed parameter supports the same properties and operations as a top-level variable of that type. The same 11 types are also legal as a declared Return a <type>, return type — parameters and returns share one vocabulary, not two. A parameter (or return type) may also be value, the dynamic type whose runtime tag travels with its payload across the call (a map rides this as payload + tag 5); see Dynamic Values (value) below.

To 'contains token' of a buffer called hay and a text called devname.
  a buffer called needle is " {devname}\n".
  a number called H is hay's size.
  a number called b is byte 1 of hay.
  ...

Key points:

  • Buffer parameters support 's size/'s empty/'s full and byte access; list parameters support 's length/element access; map parameters support 's length/'s empty/'s keys/'s values and keyed access, and print as a whole map (print holder., "{holder}"); file parameters support file properties.
  • A buffer parameter is the caller's buffer, and stays the caller's buffer across a growth: an append, a resize, or a byte written past the current capacity moves the block, and the caller's variable follows it there. Declaring a buffer of the same name inside the function names a different buffer from that point on and leaves the caller's alone; Set <parameter> to ... is not a rebinding — on a buffer it copies bytes into the buffer the parameter already names, so the caller sees the new bytes.
  • Buffers declared inside a function body work with every initializer form, including format strings (is " {devname}\n").
  • A function call's declared return type is tracked through assignment: reassigning an existing variable from a call (the label is classify of n.) preserves the correct type.

A collection parameter is the caller's collection

A list or map parameter names the caller's collection, not a copy of it. Whatever the function does to it - setting an element, appending, inserting a key, growing it past whatever size it started at - is what the caller's variable holds when the call returns, however many calls deep the collection was passed:

To 'add one to' with a list called items.
    append "x" to items.

a list called xs is ["a"].
'add one to' of xs.
'add one to' of xs.
Print xs's length.    (prints: 3)

This is the opposite of a thing parameter, which is a copy (see Things below): a thing has no reference to share, and a collection is nothing but one. Only a variable can be written back to - a collection built at the call itself ('the size of' of [1, 2, 3]) has nowhere to return growth to, so the function may read and grow it freely but the growth goes nowhere once the call ends.

A .so with a list or map parameter and the programs that see it must be built by the same version of Vox.

Function Calls

In expressions, use the function name followed by of, to, with, or on and arguments:

'add numbers' of 3 and 5
'check divisibility' of the number and 6
calculate with x and y

Rules:

  • Function name is a bare single word (calculate) or a single-quoted multi-word name ('add numbers')
  • For calls with arguments, use of, to, with, or on
  • Multiple arguments separated by and

Calls with no arguments can be written directly:

'show version'.
ping.

Calling as Statement

Print 'add numbers' of x and y.

Reading a result

Return a <type>, is optional in the grammar, but it decides where the result may be read. A declared return type travels to every call site, so the result can be printed, interpolated, stored in a list or map slot, or put in a value — each of those reads it back as what it is.

A result with no declared return type has nothing to be read as, and the compiler will not guess: it is accepted only where the position itself supplies the type, and refused where nothing does.

To 'opaque label'. Return "hi".

a text called saved is 'opaque label'.   (fine — the declaration says text)
print saved.                             (prints: hi)

print 'opaque label'.                    (compile error: no declared return type)

Positions that supply a type: a declared variable's declaration, a later assignment to one, and an argument landing on a declared parameter. Every other position — print <call>, a {...} interpolation, append, a list literal slot, set element, a map value, a value declaration — needs the return type declared. See Mixed-Type Lists.