ReferenceControlArcReferenceFunctions

Functions

Arc function reference for declaration syntax, inputs, triggers, outputs, and calling conventions

Function declaration

func name{inputs} (trigger) output {
// body
}

Table

Part Required Description
func Yes Keyword
name Yes Function identifier
{inputs} No The function inputs
(trigger) No The function trigger
output No Return type
{ body } Yes Function 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 the flow side: inputs set when you instantiate the function in a flow.
  • Parens () for the func side: 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} -> filtered

Input 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.

stage main {
    // sensor value feeds the trigger
    sensor -> filter{threshold=50.0} -> output
}

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{} -> count2

Channel 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_output

The routing table maps channel names to parameter names.

Restrictions

Table

Restriction Description
No closures Functions cannot capture variables from enclosing scope
No nested functions Functions cannot be defined inside other functions
No partial application All required arguments must be provided
Explicit returns Functions with return types require return statements
Flow inputs = constants Flow input values must be literals or channel identifiers

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)

Table

Parameter Type Description
input series or str Value to measure
Returns i64 Number of elements or bytes