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.
stage counting {
local := 0 // resets to 0 on re-entry
state $= 0 // value persists on re-entry
local + 1 -> local // always becomes 1
state + 1 -> state // increments on re-entry
}func example() {
local := 0 // resets to 0 on each call
state $= 0 // persists across calls
local = local + 1 // always becomes 1
state = state + 1 // increments: 1, 2, 3, ...
}Common Patterns
Counter
Track how many times a condition has occurred.
stage {
count $= 0
// Triggered on new `value`
value > threshold -> select{} -> {
true: count + 1 -> count
}
}func count_events{threshold f64} (value f64) i64 {
count $= 0
if value > threshold {
count = count + 1
}
return count
} Accumulator
Sum values over time.
stage accumulate {
total $= 0.0
// Triggred on new `value`
value + total -> total
}func accumulate(value f64) f64 {
total $= 0.0
total = total + value
return total
} Previous Value
Compare the current value to the previous one.
sequence phase_delta {
prev $= 0.0
// Triggered on new `value`
value - prev -> delta
value -> previous
}func rate_of_change(value f64) f64 {
prev $= 0.0
delta := value - prev
prev = value
return delta
} Running Maximum
Track the highest value seen.
stage running_max {
max $= 0.0
// Triggered on new `value`
value > max -> select{} -> {
true: value -> max
}
}func running_max(value f64) f64 {
max $= 0.0
if value > max {
max = value
}
return max
} State Toggle
Maintain an on/off state.
stage toggle {
state $= 0
// Blocked on new `trigger`
trigger -> state == 0 -> select{} -> {
true: 1 -> state,
false: 0 -> state
}
}func toggle(trigger u8) u8 {
state $= 0
if trigger {
if state == 0 {
state = 1
} else {
state = 0
}
}
return 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.