Skip to content

Control Flow

Every condition — in if, a match guard, or assert — must be a Boolean. A non-Boolean is a type error; there is no truthy coercion.

let x = 5;
if x > 10 {
print("big");
} else if x > 0 {
print("positive");
} else {
print("other");
}
FormMeaning
if c { … }Runs the block when c is true
if c { … } else { … }Runs one of the two blocks
if c { … } else if d { … }Chains another condition
if c { … } else statementThe else branch may be any one statement
if c statementShort form: one statement, no else
let level = 3;
let peaks = 0;
if level > 2 peaks += 1;
if level > 5 { print("clip"); } else print("ok");
assert peaks == 1;

With an else and blocks on both branches, if is an expression: its value is the last expression of the branch taken. There is no conditional operator (c ? a : b); use this form.

let mood = "happy";
let tempo = if mood == "happy" { 140 } else { 80 };
assert tempo == 140;
FormRuns
loop { … }Until break
do N { … }N times; N is evaluated once and truncated to an integer, and must be a Number
do N statementN times, one statement
for x in source { … }Once per element of source
loop:label { … }A labeled loop, for break:label and continue:label

break leaves the innermost loop and continue starts its next iteration. With a label, they act on the loop with that label. Only loop takes a label.

let r = 0;
loop:rows {
r += 1;
let c = 0;
loop:cols {
c += 1;
if c >= 2 { continue:rows; } // next row
if r >= 3 { break:rows; } // leave the outer loop
}
}
assert r == 3;
let n = 0;
do 2.9 { n += 1; }
assert n == 2;
do 3 n += 1;
assert n == 5;
SourceYields
range(n), range(start, end)Numbers 0 … n-1, start … end-1
Array, TupleEach element
DictEach key, in unspecified order; dict.entries() gives [key, value] pairs
IteratorEach value it yields
PatternEach event, forever — limit it with .iter().take(n)

A String is not iterable; iterate string.iter().

for i in range(2, 5) { print(i); } // 2, 3, 4
for k in #{"cutoff": 74} { print(k); } // cutoff
for pair in #["a", "b"].iter().enumerate() { print(pair[0]); }
for event in [C4 D4].iter().take(4) { print(event.note()); }

match subject { pattern => value … } tests the arms top to bottom and evaluates the first that matches. It is an expression, and also a statement. Arms are separated by newlines or commas; an arm body is an expression or a { … } block whose last expression is its value.

PatternMatches
42, "text", trueAn equal Number, String or Boolean
C4A Note, or a Number with that MIDI value (and a Note pattern matches a Number subject)
NULOnly NUL
_Anything
nameAnything, and binds it to name in the guard and body
(p1, p2, …)A tuple of that length whose elements match
pattern if conditionThe pattern, when the guard is true
fn drum_name(n) {
match n {
C2 => "kick"
D2 => "snare"
F#2 => "closed hat"
x if x > 100 => "high"
_ => "other"
}
}
assert drum_name(36) == "kick";
assert drum_name(120) == "high";
let where = match (0, 5) {
(0, 0) => "origin",
(0, y) => "on the y-axis",
_ => "elsewhere",
};
assert where == "on the y-axis";
print(drum_name(38));
  • Only Numbers, Strings, Booleans, Notes, NUL and tuples of those are matched by literal patterns. An Array, Dict, Pattern, function or iterator subject reaches only _ or a binding.
  • There is no exhaustiveness check. If no arm matches at run time, it is an error.
  • A guard that is not a Boolean is a runtime type error.

assert condition; or assert condition, message; stops the script with an assertion error when condition is false. The message may be any value; it is formatted as print() would show it.

let gain = 0.8;
assert gain >= 0.0 && gain <= 1.0, "gain must be in [0, 1]";
assert 2 > 3, "two is not greater than three";
// → Assertion failed: two is not greater than three

PLAY, PAUSE, STOP, RESET and SEEK are statements that drive the transport. See Transport.