Skip to content

Match Expressions

This content is for v0.7. Switch to the latest version for up-to-date documentation.

match selects the first arm whose pattern matches the subject and evaluates its body.

Match against numbers, strings, and booleans.

let label = match 42 {
1 => "one"
42 => "forty-two"
_ => "other"
};
PRINT label; // "forty-two"
let lang = match "resonon" {
"python" => "snake"
"resonon" => "herb"
_ => "unknown"
};
PRINT lang; // "herb"
let answer = match true {
false => "nope"
true => "yep"
};
PRINT answer; // "yep"

Note literals can be used as match patterns.

let root = C4;
let name = match root {
C4 => "C"
D4 => "D"
E4 => "E"
_ => "other"
};
PRINT name; // "C"

Notes match against their MIDI number, and numbers match against notes. C4 is MIDI note 60, so C4 and 60 are interchangeable in match patterns.

// Note subject, number pattern
let name = match C4 {
60 => "middle C"
_ => "other"
};
PRINT name; // "middle C"
// Number subject, note pattern
let name2 = match 60 {
C4 => "middle C"
D4 => "D"
_ => "other"
};
PRINT name2; // "middle C"

This is useful for dispatching on raw MIDI note numbers:

fn drum_name(note_num) {
match note_num {
C2 => "kick"
Cs2 => "side stick"
D2 => "snare"
Fs2 => "hi-hat closed"
As2 => "hi-hat open"
_ => "other"
}
}
PRINT drum_name(36); // "kick" (36 = C2)
PRINT drum_name(38); // "snare" (38 = D2)
let val = NUL;
let status = match val {
NUL => "empty"
_ => "has value"
};
PRINT status; // "empty"

Non-NUL values do not match the NUL arm:

let status2 = match 42 {
NUL => "empty"
_ => "has value"
};
PRINT status2; // "has value"

_ matches anything. Place it last to handle all remaining cases.

let result = match 999 {
1 => "one"
2 => "two"
_ => "unknown"
};
PRINT result; // "unknown"

A bare identifier captures the matched value into a variable available in the arm body.

let doubled = match 21 {
n => n * 2
};
PRINT doubled; // 42

Bindings can follow literal arms — the first match wins:

let desc = match 7 {
1 => "one"
2 => "two"
x => x + 100
};
PRINT desc; // 107

Add if <condition> after a pattern to further constrain when an arm matches. The bound variable is available in the guard expression.

let size = match 15 {
n if n > 100 => "huge"
n if n > 10 => "big"
n if n > 5 => "medium"
_ => "small"
};
PRINT size; // "big"

A guard that evaluates to false causes the arm to be skipped, and matching continues with the next arm:

let size2 = match 3 {
n if n > 10 => "big"
_ => "small"
};
PRINT size2; // "small"

The wildcard _ doesn’t bind a variable, but guards on wildcard arms can reference variables from the surrounding scope.

let threshold = 50;
let level = match 42 {
_ if threshold > 100 => "high threshold"
_ if threshold > 25 => "medium threshold"
_ => "low threshold"
};
PRINT level; // "medium threshold"
fn register(note) {
match note {
n if n < C3 => "bass"
n if n < C5 => "mid"
n if n < C7 => "treble"
_ => "ultra-high"
}
}
PRINT register(A1); // "bass"
PRINT register(E4); // "mid"
PRINT register(C6); // "treble"

Use { ... } for multi-statement arm bodies. The last expression in the block is the arm’s value.

let computed = match "calc" {
"calc" => {
let a = 10;
let b = 20;
a + b
}
_ => 0
};
PRINT computed; // 30

match can appear anywhere an expression is expected, including let bindings.

let grade = match 92 {
n if n >= 90 => "A"
n if n >= 80 => "B"
n if n >= 70 => "C"
_ => "F"
};
// Match with a computed subject
let parity = match 2 + 3 {
n if n % 2 == 0 => "even"
_ => "odd"
};
PRINT parity; // "odd"

match can also stand alone as a statement.

let out = "";
match 42 {
42 => { out = "matched 42"; }
_ => { out = "no match"; }
}
PRINT out; // "matched 42"

Resonon does not check exhaustiveness at compile time. If no arm matches at runtime, a runtime error is raised.

// Runtime error: "No match arm matched value: 3. Add a wildcard: _ => ..."
match 3 {
1 => "one"
2 => "two"
};

Match expressions can be nested inside block bodies for multi-level dispatch.

fn articulation(instrument, velocity) {
match instrument {
"strings" => {
match velocity {
v if v > 100 => "marcato"
v if v > 60 => "detaché"
_ => "pizzicato"
}
}
"brass" => {
match velocity {
v if v > 100 => "sforzando"
v if v > 60 => "sustained"
_ => "muted"
}
}
_ => "default"
}
}
PRINT articulation("strings", 120); // "marcato"
PRINT articulation("strings", 40); // "pizzicato"
PRINT articulation("brass", 80); // "sustained"
fn describe_velocity(vel) {
match vel {
v if v > 100 => "fortissimo"
v if v > 80 => "forte"
v if v > 60 => "mezzo-forte"
v if v > 40 => "piano"
_ => "pianissimo"
}
}
for v in #[30, 64, 90, 120] {
PRINT f"{v} -> {describe_velocity(v)}";
}
// 30 -> pianissimo
// 64 -> mezzo-forte
// 90 -> forte
// 120 -> fortissimo

Match a mode name to select scale intervals.

fn scale_intervals(mode) {
match mode {
"ionian" => #[0, 2, 4, 5, 7, 9, 11]
"dorian" => #[0, 2, 3, 5, 7, 9, 10]
"phrygian" => #[0, 1, 3, 5, 7, 8, 10]
"mixolydian" => #[0, 2, 4, 5, 7, 9, 10]
"aeolian" => #[0, 2, 3, 5, 7, 8, 10]
_ => #[0, 2, 4, 5, 7, 9, 11]
}
}
let intervals = scale_intervals("dorian");
PRINT intervals; // #[0, 2, 3, 5, 7, 9, 10]

Map General MIDI drum note numbers to names using note–number coercion.

fn drum_label(n) {
match n {
C2 => "kick"
D2 => "snare"
Fs2 => "hi-hat closed"
As2 => "hi-hat open"
C3 => "high tom"
_ => f"drum {n}"
}
}
for note in #[36, 38, 42, 46, 48, 99] {
PRINT f"{note}: {drum_label(note)}";
}
// 36: kick
// 38: snare
// 42: hi-hat closed
// 46: hi-hat open
// 48: high tom
// 99: drum 99