Control Flow
These operations are Controls: their arguments are not all evaluated before the call.
| Control | Result |
|---|---|
if(condition, then) | Selected branch result, or no value |
if(condition, then, else) | Selected branch result |
while(condition, body) | No value |
for_each(array, name, body) | No value |
chain(name, initial, step...) | Final value |
if
Evaluates the condition once. It must produce a boolean. Evaluates only the selected branch and returns its result. A false condition with no else branch produces no value. Branches can be expressions or blocks.
declare(score, integer, 12)
declare(message, string, if(greater(score, 10), "passed", "try again"))
print(message) / passed
A branch does not create a scope on its own. A block used as a branch does.
while
Evaluates the condition before each iteration. While it is true, evaluates the body, discards its result, and checks the condition again. The condition must produce a boolean every time. A false initial condition skips the body.
declare(counter, integer, 0)
while(less(counter, 3), {
print(counter)
set(counter, add(counter, 1))
})
This prints 0, 1, and 2. There are currently no stdlib break or
continue operations. An error in the condition or body stops the loop.
for_each
Evaluates the array once and takes a snapshot of its element values before running the body. The second argument is a literal identifier. Each iteration gets a fresh child scope with that name bound to the current element; the body result is discarded.
declare(items, array(integer), array_of(2, 4, 6))
declare(total, integer, 0)
for_each(items, item, set(total, add(total, item)))
print(total) / 12
Adding, replacing, or removing elements in the source array does not change the snapshot. Object elements still refer to the same objects, so their mutations remain visible. See reference semantics. The iteration binding shadows an outer variable of the same name and does not change its value. A non-array input is an error.
chain
Evaluates the initial expression once. For each following step, creates a fresh child scope binding the literal name to the preceding value, evaluates the step, and uses its value as the next input. Returns the last value. With no steps, returns the initial value.
print(chain(link, array_of(10, 20),
get_at(link, 1),
add(link, 2),
to_string(link),
concat("Result: ", link)
)) / Result: 22
The value can change type between steps. Every step must return a runtime value;
none is allowed, but a type description or an expression with no value is an
error. The temporary binding does not escape the step or replace an outer
variable of the same name. Mutations to outer variables and shared objects
remain visible.
Use this for mixed array/record traversal as well as value transformations. For paths containing only arrays or only records, get_at and get_field accept multiple path segments directly.