Functions
Arc function reference for declaration syntax, inputs, triggers, outputs, and calling conventions
Function declaration
func name{inputs} (trigger) output {
// body
}Basic functions
A function with inputs and an output:
func add(x f64, y f64) f64 {
return x + y
}A function with no return value:
func log_value(value f64) {
debug_channel = str(value)
}Inputs
- Braces
{}for theflowside: inputs set when you instantiate the function in a flow. - Parens
()for thefuncside: inputs passed when you call the function.
Here threshold is set in the braces and value is the trigger:
func filter{threshold f64} (value f64) f64 {
if value > threshold {
return value
}
return 0.0
}
sensor -> filter{threshold = 0.1} -> filteredInput parameters can be literals or channel references:
func controller{
setpoint f64, // literal or channel
input chan f64, // channel reference
output chan f64, // channel reference
} (enable bool) f64 {
value := input
if enable {
output = value - setpoint
}
return value
}
// Static setpoint
enable_cmd -> controller{setpoint=100.0, input=temp, output=cmd}
// Setpoint driven by a channel
enable_cmd -> controller{setpoint=setpoint_input, input=temp, output=cmd}Optional inputs
Optional inputs have default values and must be trailing:
func clamp(value f64, min f64 = 0.0, max f64 = 1.0) f64 {
if value < min {
return min
}
if value > max {
return max
}
return value
}Output types
Single unnamed output
func double(value f64) f64 {
return value * 2
}Single named output
func compute(x f64) result f64 {
result = x * 2
return result
}Multiple named outputs
A function may declare more than one named output. Each output keys a branch at the call site: setting an output runs its routing table entry.
func split(value f64) (high f64, low f64) {
if value > 50.0 {
high = value
} else {
low = value
}
}
sensor -> split{} -> {
high: "high" -> log,
low: "low" -> log
}Calling functions
Arc has two calling conventions depending on context: parentheses inside function bodies, and braces to instantiate a function in a flow statement.
Flow
Func
stage main {
// sensor value feeds the trigger
sensor -> filter{threshold=50.0} -> output
}func outer(x f64) f64 {
y := inner(x, 0.0, 100.0) // function call
return y
}Stateful variables in functions
Functions can declare stateful variables that persist across invocations:
func counter() i64 {
count $= 0 // persists
count = count + 1
return count
}Each function instantiation has its own state:
// Two separate counters with independent state
time.interval{100ms} -> counter{} -> count1
time.interval{200ms} -> counter{} -> count2Channel operations in functions
Functions can read from and write to channels:
func controller{input chan f64, output chan f64, threshold f64} () {
value := input // read (non-blocking)
if value > threshold {
output = value // write (queued)
}
}Functions can also reference global channels by name:
func monitor() {
if ox_pt_1 > 500 {
alarm_status = true
}
}Channel reads in function bodies are non-blocking snapshots. They return the latest value or zero if no data exists.
Multi-input functions
Use routing tables to map multiple sources to input parameters:
func average(a f64, b f64) f64 {
return (a + b) / 2.0
}
{
sensor1: a,
sensor2: b
} -> average{} -> avg_outputThe routing table maps channel names to parameter names.
Restrictions
Recursion
Recursion is allowed but must have a termination condition:
func factorial(n i64) i64 {
if n <= 1 {
return 1
}
return n * factorial(n - 1)
}Unbounded recursion causes a stack overflow at runtime.
Built-in functions
Built-in functions require no import or module prefix.
len
Returns the length of a series or string.
data := [1.0, 2.0, 3.0]
length := len(data) // 3 (i64)
msg := "hello"
chars := len(msg) // 5 (i64)