Variables
Arc variable reference for literal, stateful, channel-read, and channel read/write variables, assignment, and scope
Declaration
A variable names a value. Declare one with :=, or $= to make it stateful. In the
flow context, a variable takes one of three kinds.
Assignment
Assign to existing variables with =:
x = x + 1Supported compound assignment:
count += 1 // count = count + 1
value -= 10 // value = value - 10
total *= 2 // total = total * 2
ratio /= 4 // ratio = ratio / 4
remainder %= 3 // remainder = remainder % 3Scope
A variable is visible from its declaration to the end of its enclosing block, and nested blocks can read variables from enclosing scopes. A variable is usable only after its declaration, including across stages.
Flow
Func
sequence main {
// visible for the rest of the sequence
x := 10
stage s1 {
// visible only inside the stage
y := x + 1
}
// y is not visible here
}func example() {
// visible for the rest of the function
x := 10
if x > 5 {
// visible only inside the if block
y := x + 1
}
// y is not visible here
}Top-level variables are global constants and cannot be reassigned or written to. Stateful variable declarations are not allowed at the top level.
Variable types
Literal
A literal variable holds a value. Declare it with :=; the type is inferred from the
value, or you can annotate it. A literal resets to its initial value on each entry into
its scope.
MAX_PRESSURE := 500.0 // f64
SAMPLE_COUNT := 100 // i64
SETPOINT f64 := 50.0 // explicit type
TIMEOUT := 30s // with unit suffix
ENABLED := true // boolStateful
A stateful variable keeps its value across scope entries instead of resetting. Declare
it with $=. Only literal variables can be stateful.
count $= 0 // persists across entriesStateful variables drive counters, accumulators, and edge detection.
Counter
Track how many times a condition has occurred.
Flow
Func
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.
Flow
Func
stage accumulate {
total $= 0.0
// Triggered 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.
Flow
Func
sequence phase_delta {
prev $= 0.0
// Triggered on new `value`
value - prev -> delta
value -> prev
}func rate_of_change(value f64) f64 {
prev $= 0.0
delta := value - prev
prev = value
return delta
} Running maximum
Track the highest value seen.
Flow
Func
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.
Flow
Func
stage toggle {
state $= false
// Triggered on new `trigger`
trigger -> not state -> select{} -> {
true: true -> state,
false: false -> state
}
}func toggle(trigger bool) bool {
state $= false
if trigger {
state = not state
}
return state
}Channel read/write
A channel read/write variable names a channel directly. Reads and writes pass through to
that channel, so it behaves exactly like the channel it names. Reassign it with = to
rebind it to another channel of the same type.
Readable names
A short local name for a long or deeply namespaced channel.
cpu := sy_node_1_metrics_cpu
cpu > 90.0 => throttle Actuator failover
Command an actuator through an alias; on a fault, rebind it to the backup. Writes stay wired to the alias and follow whichever channel is bound.
stage main {
pump := pump_a_cmd
sequence run {
stage primary {
pump_a_fault => backup
}
stage backup {
pump = pump_b_cmd // reroute commands to the backup pump
}
}
// writes follow whichever pump is bound
run_cmd -> pump
} Clamp a setpoint
Read and write the same channel: watch an operator-writable setpoint and write a safe limit back when it exceeds a bound.
setpoint := setpoint_cmd
sequence guard {
stage watch {
setpoint > 500.0 => clamp
}
stage clamp {
500.0 -> setpoint // write a safe limit back
setpoint <= 500.0 => watch
}
} Device selection
Point the same routine at a chosen unit; rebind the alias to switch targets without rewiring the flow.
stage main {
dut := dut_a_cmd // default target
sequence run {
stage select {
use_b => on_b // pick unit B instead
}
stage on_b {
dut = dut_b_cmd
}
}
run_cmd -> dut // commands follow whichever unit is bound
}Channel read
A channel read variable is a read-only expression over one or more channels. It is
reactive: when any channel it reads produces new data, the variable updates and every
flow that reads it runs again with the new value.
Writing to a channel-read variable throws a compile error (value -> scaled).
Reassigning it with = is not a plain value replacement; it rebinds the expression, and
the variable follows the new rule from its next trigger onward.
Logging
A status label that reflects the current stage and the live reading, logged as it streams.
stage monitor {
reactive_expr := "Nominal: " + str(ai_0)
sequence main {
stage nominal {
reactive_expr = "Nominal: " + str(ai_0)
}
stage low {
reactive_expr = "Low: " + str(ai_0)
}
stage high {
reactive_expr = "High: " + str(ai_0)
}
}
// Updates on underlying `ai_0`
reactive_expr -> log
} Gain scheduling
A proportional command whose gain changes with the phase, feeding the proportional valve directly.
stage control {
// proportional command; the gain changes with the phase
command := coarse_gain * (setpoint - pressure)
sequence pressurize {
stage fill {
pressure > 400.0 => trim
}
stage trim {
// gentler gain near the target
command = fine_gain * (setpoint - pressure)
}
}
command -> valve_cmd
} Sensor failover
Read a scaled sensor; on a fault, reassign the same variable to a backup probe. Downstream flows stay wired to the variable.
stage monitor {
temp := probe_a * 0.1 - 40.0
sequence acquire {
stage primary {
probe_a_fault => failover
}
stage failover {
// identical scaling, backup probe
temp = probe_b * 0.1 - 40.0
}
}
temp -> temperature
}Shadowing
Arc does not allow shadowing. A variable cannot have the same name as one in an enclosing scope:
Flow
Func
sequence example {
x i32 := 10
stage s1 {
x i32 := 20 // Error: conflicts with outer x
}
}func example() {
x i32 := 10
if x > 0 {
x i32 := 20 // Error: conflicts with outer x
}
}Use a different name or reassign the existing variable:
Flow
Func
sequence example {
x i32 := 10
stage s1 {
inner_x i32 := 20 // different name
x = 20 // reassign existing
}
}func example() {
x i32 := 10
if x > 0 {
inner_x i32 := 20 // different name
x = 20 // reassign existing
}
}Local variables can shadow built-in functions and constants:
Flow
Func
sequence example {
min := 10 // shadows built-in min function
}func example() {
min := 10 // shadows built-in min function
}