Values & Variables
Before you can sequence a note, Resonon has to know what a note is. This chapter covers the raw material you build music from: the literal values you can write down, the variables that hold them, and the one rule about copying that trips up newcomers most often. Everything here is plain programming-language groundwork — but it is groundwork you will lean on constantly while live-coding.
Here is a taste. Every line below is a value bound to a name; run it and you’ll see the string printed back:
let bpm = 120; // a Numberlet title = "first sketch"; // a Stringlet root = C4; // a Notelet riff = [C4 E4 G4 C5]; // a Patternlet scale = #[0, 2, 4, 5, 7]; // an Array
PRINT title + " at " + bpm + " BPM";Now let’s take the pieces apart.
Comments
Section titled “Comments”Resonon supports three styles of comment.
// Single-line comment
/* Multi-line comment */
/// Doc comment — attached to the next function definition, and shown by show().fn double(x) { return x * 2;}Use // for quick notes, /* */ to block out a region, and /// to document a function — the
doc comment is what show() displays when you inspect that function in a session.
Literals
Section titled “Literals”A literal is a value written out directly in your code.
Numbers
Section titled “Numbers”Integer, decimal, scientific, hexadecimal, and binary notation are all valid. Hex and binary literals accept underscore separators for readability.
PRINT 42; // integerPRINT 3.14; // decimalPRINT 1e-3; // scientific notation (0.001)PRINT 2.5e2; // scientific notation (250)PRINT .5; // leading dot (0.5)PRINT 5.; // trailing dot (5.0)PRINT 0xFF; // hexadecimal (255)PRINT 0b1010; // binary (10)PRINT 0xFF_FF; // hex with separator (65535)PRINT 0b1010_0011; // binary with separator (163)Resonon has a single Number type — there is no separate integer type, so 5 and 5.0 are the
same value.
Strings
Section titled “Strings”Strings are delimited by double quotes. The + operator concatenates them. For methods and
format strings, see Strings.
PRINT "Hello, " + "RESONON!"; // concatenation with +Booleans
Section titled “Booleans”PRINT true;PRINT false;Truthiness
Section titled “Truthiness”Only false and NUL are falsy — everything else is truthy, including values that are falsy
in many other languages (0, the empty string, the empty array):
// .filter() keeps elements whose callback returns a truthy value.// 0, "" and #[] are all truthy — only false and NUL are falsy.PRINT #[0, "", false, NUL, #[]].filter(fn(x) { return x; });// → [0, "", []]Truthiness is what higher-order methods like .filter(), .find(), .any(), and .all() test
when they call your callback.
NUL represents the absence of a value. Undefined variables evaluate to NUL.
PRINT NUL; // NUL
PRINT NUL == NUL; // truePRINT 5 == NUL; // falsePRINT NUL == false; // false — NUL is not falsePRINT NUL == 0; // false — NUL is not zeroMissing function arguments default to NUL, and a function with no explicit return yields
NUL:
fn greet(name) { if name != NUL { PRINT "Hello, " + name; }}greet(); // name is NUL — prints nothing
fn no_return() { let x = 1; }PRINT no_return(); // NULUnlike SQL’s NULL, NUL does not propagate through arithmetic — using it as an operand is a
type error. It can, however, live inside collections perfectly well:
let items = #[1, NUL, 3];let config = #{ "key": NUL };PRINT items; // [1, NUL, 3]PRINT config; // {"key": NUL}A note literal is a letter, an optional accidental, and an octave: Name Accidental? Octave.
Accidentals are # (sharp) and b (flat) — there is no s form. Octaves range from -1 to 9.
PRINT C4; // middle CPRINT A4; // concert A (440 Hz by default)PRINT Bb3; // B-flat in octave 3PRINT D#5; // D-sharp in octave 5PRINT C-1; // lowest MIDI notePRINT G9; // highest MIDI noteNotes map to MIDI values 0–127 (C-1 through G9); a note outside that range is a parse error. They compare against Numbers by MIDI value:
PRINT C4 == 60; // truePRINT D4 > C4; // truePRINT A4 >= 69; // trueAn underscore _ is a rest (silence). Inside a pattern it occupies time like any other step:
let offbeat = [C4 _ E4 _]; // play, rest, play, restPRINT offbeat;Outside a pattern, a bare _ evaluates to NUL:
let r = _;PRINT r; // NULPRINT r == NUL; // trueVariables
Section titled “Variables”Declare variables with let. Resonon is dynamically typed, so any variable can hold any value,
and can be reassigned to a different type later.
let x = 10;let name = "RESONON";let melody = [C4 D4 E4];
x = "now a string"; // reassignment can change the typePRINT x;Compound assignment
Section titled “Compound assignment”let count = 1;count += 1; // 2count *= 3; // 6PRINT count;Block scoping
Section titled “Block scoping”A let inside a block is local to that block, and an inner block can shadow an outer variable:
let x = "outer";{ let x = "inner"; // shadows the outer x PRINT x; // "inner"}PRINT x; // "outer"Sharing & Cloning
Section titled “Sharing & Cloning”This is the one rule worth burning into memory. Containers — arrays, dictionaries, and class instances — share on assignment. Two variables then refer to the same underlying data, and a change through one is visible through the other:
let a = #[1, 2, 3];let b = a; // b shares the same arrayb.push(99);PRINT a; // [1, 2, 3, 99] — both see the changeFunction parameters share too — no special syntax needed:
fn append_high(arr) { arr.push(C5);}let melody = #[C4, E4, G4];append_high(melody);PRINT melody; // [C4, E4, G4, C5] — the caller's array changedTo work on an independent copy, call .clone():
let a = #[1, 2, 3];let b = a.clone(); // b is a separate copyb.push(99);PRINT a; // [1, 2, 3] — unchangedPRINT b; // [1, 2, 3, 99]Primitives — numbers, strings, booleans, notes, and NUL — are always copied by value, so this
sharing only ever applies to containers.
Type coercion
Section titled “Type coercion”Resonon is strict: most operations require matching types, with no implicit coercion. The handful of cross-type rules are worth knowing:
+also concatenates strings (coercing the other side to a string) and transposes events==/!=work cross-type for Note↔Number and NUL↔anything</>/<=/>=work for Note↔Number comparisons&&/||require Boolean operands- anything else across mismatched types is a type error
PRINT C4 == 60; // true — Note↔Number by MIDI valuePRINT "got: " + 1; // "got: 1" — Number coerced to StringThe full operator behaviour — including the type-compatibility table — is in Operators.
Next Steps
Section titled “Next Steps”You now have the values and the variables to hold them. Next, learn how to combine them.
- Operators — arithmetic, comparison, logical, and the musical
<</>>operators - Collections — arrays and dictionaries in depth
- Music Theory — turn notes into scales, keys, and chords