Stateful Variables
How to persist values across executions using Arc's stateful variables
Arc functions execute repeatedly in response to incoming data. Each execution starts fresh, with local variables reset to their initial values. Stateful variables let you preserve values across these executions.
Local vs Stateful Variables
Use := for local variables that reset each execution, and $= for stateful variables
that persist:
func example() {
local := 0 // resets to 0 every execution
state $= 0 // persists across executions
local = local + 1 // always becomes 1
state = state + 1 // increments: 1, 2, 3, ...
} The $= operator declares a stateful variable with an initial value. After the first
execution, subsequent executions use the persisted value instead of the initial value.
Common Patterns
Counter
Track how many times a condition has occurred:
func count_events{threshold f64} (value f64) i64 {
count $= 0
if value > threshold {
count = count + 1
}
return count
} Accumulator
Sum values over time:
func accumulate(value f64) f64 {
total $= 0.0
total = total + value
return total
} Previous Value
Compare the current value to the previous one:
func rate_of_change(value f64) f64 {
prev $= 0.0
delta := value - prev
prev = value
return delta
} Running Maximum
Track the highest value seen:
func running_max(value f64) f64 {
max $= 0.0
if value > max {
max = value
}
return max
} State Toggle
Maintain an on/off 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.
Reset on Stage Re-entry
When a stage in a sequence is re-entered, all stateful variables in that stage reset to their initial values. This ensures stages start fresh when you transition back to them.
sequence main {
stage monitoring {
// count resets to 0 each time we enter monitoring
sensor -> count_events{threshold=100.0} -> event_count
event_count > 10 => alert
}
stage alert {
// handle alert...
acknowledged => monitoring // re-entering resets count
}
} If you need to preserve state across stage re-entries, put the stateful logic in a function that’s called from the top level (outside any sequence).
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.