Nodora
Language

Constants

Top-level named values shared across rules

A constant is a top-level named value declared with const. Unlike a local binding, a constant lives outside any rule and can be referenced by every rule that follows it.

const MAX_SCORE = 100
const BLOCKED   = ["spam", "abuse"]

rule Moderate {
    out flagged = input.score > MAX_SCORE
    out blocked = input.category in BLOCKED
}

Syntax

const NAME = <expression>

The name follows the usual identifier rules. The expression must be a constant expression.

Constant expressions

A constant's value is computed at compile time, so it may only use things that are known then. The following are allowed:

  • Literals: numbers, strings, booleans, arrays, and objects.
  • Operators (+, &&, ==, in, …) and conditionals (if/then/else, ? :).
  • Indexing and field access on constant arrays and objects.
  • References to constants declared above.
  • Calls to pure built-in functions.
const LIMIT   = 60 * 60          // arithmetic
const REGIONS = ["us", "eu"]     // array literal
const CONFIG  = { retries: 3 }   // object literal
const RETRIES = CONFIG.retries   // field access on a constant
const ABS     = math::abs(-5)    // pure built-in call
const TIER    = LIMIT > 1000 ? "high" : "low"

Anything that depends on runtime state is rejected:

const BAD1 = input.score          // error: reads input
const BAD2 = time::now()          // error: impure built-in
const BAD3 = |x| x + 1            // error: not a constant value

A constant cannot read input, call a function whose output varies at runtime (such as time::now()), or hold a lambda or match expression.

Ordering

Constants are resolved top to bottom. A constant or rule may reference only the constants declared before it. Forward references are a compile-time error, the same as for local bindings.

const A = 10
const B = A * 2            // ok, A is declared above

rule Early { out x = C }   // error: 'C' is not defined yet
const C = 5

Scope

Constants are global to the file and visible to every later rule. They are fixed before any rule runs, so a constant cannot see rule-local names.

A rule may introduce a local with the same name as a constant. The local shadows the constant inside that rule:

const RATE = 1

rule Custom {
    RATE  = input.rate     // local 'RATE' shadows the constant here
    out r = RATE
}

Naming

A constant name must be unique among all top-level declarations: it cannot clash with another constant, a rule, a signal, or the reserved name input.

Runtime cost

Because constants are evaluated at compile time, referencing one has no runtime cost: the compiler folds the value into each use. A constant that no rule references is dropped and adds nothing to the compiled output.

On this page