Variables
let and var, block scope, and why a var may be assigned inside a branch.
Anyone writing more than one line inside a frame.
Locals are declared with let or var. A bare type name is not a local
declaration — float x; at statement level is rejected, because in a processor
that spelling means state.
let x = in * 2.0f; // immutable
var y = 0.0f; // mutable
y = y + 1.0f;Choose the simplest kind of memory#
Use let for a value that is computed once during the current pass. It gives
the expression a name and prevents accidental reassignment. Use var when an
algorithm updates a local result, such as accumulating a sum or finding the
largest table entry. Neither one remembers its value for the next audio frame.
In the short example above, x names an amplified input. y begins at zero
and becomes one. On the next frame the declaration runs again, so y becomes
one again, not two. A counter that must keep counting belongs at processor
scope.
A var may be assigned inside a branch#
float t[8];
var best = 0.0f;
for (wrap<8> i) { if (t[i] > best) { best = t[i]; } }This used to be rejected. It works because a var is lowered into a memory
slot rather than an SSA value, so nothing has to be joined at a branch merge;
LLVM's mem2reg puts it back in a register afterwards. The restriction bought
nothing and cost the most natural way to write a search.
Scope#
Locals are block-scoped, and may not shadow an endpoint or a state variable. Shadowing a name that means something else in the same processor is the kind of bug that reads correctly, so it is refused rather than allowed.
Locals are not state#
A local lives for one frame. It is not zeroed between frames so much as
simply not the same variable — each frame's main body is a fresh pass.
If you want a value to survive to the next sample, it has to be processor state. That is the single most common thing to get wrong when arriving from a language where everything persists by default:
// Wrong: `phase` restarts every frame, so this is silence with a DC offset.
var phase = 0.0f;
phase = phase + 0.01f;
out <- sin (phase);The fix is to move phase to processor scope, where it persists. The
oscillator on the first-patch page does
exactly that.