Variables & Types
Comments
Section titled “Comments”Resonon supports three styles of comment.
// Single-line comment
/* Multi-line comment */
/// Doc comment — attached to the next function definition./// Displayed by show().fn double(x) { return x * 2;}Literals
Section titled “Literals”Numbers
Section titled “Numbers”Integer, decimal, scientific, hexadecimal, and binary notation are all valid number literals. Hex and binary literals support underscore separators for readability.
42 // integer3.14 // decimal1e-3 // scientific notation (0.001)2.5e2 // scientific notation (250).5 // leading dot (0.5)5. // trailing dot (5.0)0xFF // hexadecimal (255)0b1010 // binary (10)0xFF_FF // hex with separator (65535)0b1010_0011 // binary with separator (163)Strings
Section titled “Strings”Strings are delimited by double quotes. See Strings for methods and format strings.
"hello world""Hello, " + "RESONON!" // concatenation with +Booleans
Section titled “Booleans”truefalseTruthiness
Section titled “Truthiness”Only false and NUL are falsy — everything else is truthy, including values that are falsy in many other languages:
// .filter() keeps elements whose callback returns a truthy value.// 0, "", and #[] are all truthy — only false and NUL are falsy.#[0, "", false, NUL, #[]].filter(fn(x) { return x; });// → [0, "", []]Truthiness is used by higher-order methods like .filter(), .find(), .any(), and .all(), which test the return value of their callback for truthiness.
NUL represents the absence of a value. Undefined variables evaluate to NUL.
PRINT NUL; // NUL
NUL == NUL; // true5 == NUL; // falseNUL == false; // falseNUL == 0; // false
// Check whether a variable is definedif x != NUL { PRINT "x is defined";}Missing function arguments default to NUL, and functions without an explicit return yield 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(); // NUL
// NUL does NOT propagate through operations (unlike SQL NULL)NUL + 1; // type errorNUL * 2; // type error
// But NUL can be stored in collections normallylet items = #[1, NUL, 3];let config = #{ "key": NUL };Notes follow the pattern Name Accidental? Octave where the octave ranges from -1 to 9.
C4 // middle CA4 // concert A (440 Hz by default)Bb3 // B-flat in octave 3D#5 // D-sharp in octave 5C-1 // lowest MIDI noteG9 // highest MIDI noteNotes map to MIDI values 0–127 (C-1 through G9). Notes outside this range produce a parse error. In patterns, transposition can temporarily exceed 0–127; clamping happens at MIDI output.
Notes compare with Numbers by MIDI value:
C4 == 60; // trueD4 > C4; // trueA4 >= 69; // trueDirect note arithmetic (C4 + 2) is a type error — transposition works through pattern combinators.
An underscore _ represents a rest (silence) inside a pattern.
[C4 _ E4 _ G4] // notes with rests between themOutside a pattern, _ creates a standalone Rest value that can be stored and passed around.
let r = _;PRINT r; // _
let items = #[C4, _, E4]; // rests can live in arrays and dictsPRINT items[1]; // _
// Rest is truthy (e.g. kept by .filter())#[C4, _, E4].filter(fn(x) { return x; }); // [C4, _, E4]
// Arithmetic with rests is a type error_ + 1; // error: cannot add Rest and NumberVariables
Section titled “Variables”Declare variables with let. Resonon is dynamically typed, so any variable can hold any value.
let x = 10;let name = "RESONON";let melody = [C4 D4 E4];Reassignment and dynamic typing
Section titled “Reassignment and dynamic typing”Variables can be reassigned to a different type at any time.
let val = 42;val = "now a string";val = [C4 E4 G4];Compound assignment
Section titled “Compound assignment”let count = 1;count += 1; // 2Block scoping
Section titled “Block scoping”Variables declared with let inside a block are local to that block. Inner blocks can shadow outer variables.
let x = "outer";{ let x = "inner"; // shadows outer x PRINT x; // "inner"}PRINT x; // "outer"Copy-on-assignment
Section titled “Copy-on-assignment”Assignment always creates an independent deep copy. To share state, use References.
let a = #[1, 2, 3];let b = a; // b is a separate copyb.push(99);PRINT a; // [1, 2, 3] — unchangedPRINT b; // [1, 2, 3, 99]Type Coercion
Section titled “Type Coercion”Resonon is strict — most operations require matching types and there is no implicit coercion. A few key cross-type rules exist:
+also concatenates strings and transposes events==/!=work cross-type for Note↔Number and NUL↔anything</>/<=/>=work for Note↔Number comparisons&&/||require Boolean operands- Mismatched types in other operations produce a type error
C4 == 60; // true — Note↔Number comparison by MIDI value"hi" + 1; // type error — no implicit number-to-stringtrue && 0; // type error — && requires BooleansSee Operators for the full operator reference.
Compound types
Section titled “Compound types”Resonon has two built-in collection types:
- Arrays — ordered collections created with
#[...]orArray(...). See Arrays. - Dictionaries — key-value maps created with
#{...}. See Dictionaries.
References {#references}
Section titled “References {#references}”By default, assignment in Resonon creates an independent deep copy. References provide an opt-in mechanism for shared mutable state when you need multiple variables to see the same data.
Creating references
Section titled “Creating references”Use the & operator on an array, dictionary, or class instance. Only arrays, dicts, and class instances can be referenced — not primitives.
let nums = #[1, 2, 3];let nums_ref = &nums;Dict references
Section titled “Dict references”Dictionaries can be referenced just like arrays. Mutations through the reference are visible to all variables sharing the same dict.
let config = #{channel: 1, velocity: 100, transpose: 0};let config_ref = &config;
config_ref.set("velocity", 80);PRINT config["velocity"]; // 80
config_ref.set("transpose", 12);PRINT config["transpose"]; // 12Mutating through references
Section titled “Mutating through references”Changes made through a reference are visible to all variables that share the same underlying value.
let nums = #[1, 2, 3];let nums_ref = &nums;
nums_ref.push(4);PRINT nums; // [1, 2, 3, 4]
nums_ref.pop();PRINT nums; // [1, 2, 3]References with classes
Section titled “References with classes”class Counter { let count;
fn new(initial) { this.count = initial; }
fn get() { return this.count; }
fn increment() { this.count += 1; }
fn add(n) { this.count += n; }}
let counter = Counter(0);let counter_ref = &counter;
counter_ref.increment();PRINT counter.get(); // 1
counter_ref.add(10);PRINT counter.get(); // 11Reference parameters
Section titled “Reference parameters”Declare a reference parameter with & in the function signature. Pass a reference as the argument.
fn double_counter(&c) { c.add(c.get());}
let my_counter = Counter(5);let ref_counter = &my_counter;
double_counter(ref_counter);PRINT my_counter.get(); // 10Shared references
Section titled “Shared references”Multiple references to the same value all see the same mutations.
let shared = #[1, 2, 3];let ref1 = &shared;let ref2 = &shared;
ref1.push(100);ref2.push(200);
PRINT shared; // [1, 2, 3, 100, 200]Transparent field access
Section titled “Transparent field access”Fields and methods work through references automatically (auto-deref). No special syntax is needed.
class Point { let x; let y;
fn new(x_val, y_val) { this.x = x_val; this.y = y_val; }
fn move_by(dx, dy) { this.x += dx; this.y += dy; }}
let point = Point(0, 0);let point_ref = &point;
point_ref.move_by(5, 10);PRINT point.x; // 5PRINT point.y; // 10Equality and references
Section titled “Equality and references”The == operator only works on primitive types: Number, String, Boolean, Note, and NUL. Comparing arrays, dicts, class instances, or references with == is a type error, not false.
Debugging references
Section titled “Debugging references”The type() function reports reference types with a & prefix:
let nums = #[1, 2, 3];let nums_ref = &nums;PRINT type(nums_ref); // &ArrayReferences in script patterns
Section titled “References in script patterns”References cannot be captured inside script pattern callbacks. Use plain variables (deep copies) instead.