ReferenceControlArcConceptsStateful Variables

Stateful Variables

How to persist values across executions using Arc's stateful variables

Arc programs execute repeatedly in response to incoming data. Each time a scope runs (a function call, or entering a stage or sequence), its local variables (:=) reset to their initial values. Stateful variables ($=) let you preserve values across those runs.

Local vs Stateful Variables

Use := for local variables that reset each run, and $= for stateful variables that persist across scope re-entry.

Common Patterns

Counter

Track how many times a condition has occurred.

Accumulator

Sum values over time.

Previous Value

Compare the current value to the previous one.

Running Maximum

Track the highest value seen.

State Toggle

Maintain an on/off state.

Loops vs. Stateful Variables

Arc gives you two tools for repeated work, and they serve different purposes.

for loops do work within a single function call, iterating over a series, counting through a range, or repeating until a condition is met:

func apply_calibration(raw f64) f64 {
    offsets := [0.1, -0.05, 0.03]
    correction f64 := 0.0
    for x := offsets{correction=correction + x}
    return raw + correction
}

Stateful variables accumulate data across executions. The reactive model calls your function once per incoming value, and stateful variables carry the running totals between calls:

func running_average(value f64) f64 {
    total $= 0.0
    count $= 0
    total = total + value
    count = count + 1
    return total / f64(count)
}

Use for loops for computation within a single call. Use stateful variables for streaming computations that build up over time.

Type Inference

Stateful variables infer their type from the initial value, just like local variables. You can also specify the type explicitly:

func example() {
    // Type inferred from initial value
    count $= 0 // i64 (integer literal default)
    total $= 0.0 // f64 (float literal default)
    // Explicit type annotation
    precise f32 $= 0.0 // f32 instead of f64
}

Integer literals default to i64 and float literals default to f64. If you need a different type, add an explicit annotation.