Skip to content

Values & Types

Resonon is dynamically typed: a value has a type, a variable does not. Operators on values are in Operators; collections are in Collections & Iterators. Why containers share and primitives copy is in Values & Sharing.

SyntaxKind
// …Line comment
/* … */Block comment
/// …Doc comment for the definition below it
//! …Doc comment for the file; only at the top of the file
//// …Line comment (four or more slashes)
//! File documentation — what this file is.
// Line comment
/* Block
comment */
/// Doubles a number.
fn double(x) {
return x * 2;
}
print(double(4));

Doc comments are shown by show(), editor hover, and resonon pkg inspect.

  • /// documents the next fn, let, class, class method or dsp definition. Blank lines and ordinary comments in between change nothing; consecutive /// blocks join into one.
  • A /// with nothing below it to document — above a use, an expression, a class field, inside a block, or at the end of the file — is an error.
  • A //! anywhere but the top of the file is an error.
  • A doc comment with an Experimental: line marks what it documents as experimental, with the text after it saying what may change: show(), editor hover and completion say so. In a file’s //!, it marks everything the file defines.
TypeLiterals
Number42, 3.14, .5, 5., 1e-3, 2.5e2, 0xFF, 0b1010, 0xFF_FF, 0b1010_0011
String"text", f"text {expr}"
Booleantrue, false
NULNUL
NoteC4, F#3, Bb2, C-1 … G9
Rest_
Pattern[C4 E4 G4], <C4 E4>, {C4 D4 E4} — see Mini-Notation Grammar
Array#[1, 2, 3]
Dict#{"key": 1, 36: "kick"}
Tuple(1, "a")
Functionfn(x) { return x * 2; }

There is one Number type: 5 and 5.0 are the same value. Hex and binary literals accept _ separators.

Strings are double-quoted and immutable; every string method returns a new string. The methods are in the String reference.

EscapeCharacter
\"Double quote
\\Backslash
\nNewline
\rCarriage return
\tTab

An f prefix makes a format string: any expression inside {…} is evaluated and formatted as print() would show it. {{ and }} are literal braces. String literals may appear inside the braces.

let vel = 100;
let notes = #["C4", "E4", "G4"];
print(f"Dynamics: {if vel > 80 { "loud" } else { "soft" }}");
print(f"Root: {notes[0]}, count: {notes.length()}");
print(f"Literal braces: {{like this}}");
assert "Tempo: " + 120 == "Tempo: 120"; // + also concatenates

A string is not directly iterable: for ch in "abc" is an error; iterate "abc".iter(), which yields one-character strings.

if conditions, match guards, assert, &&, || and ! require an actual Boolean; any other value is a type error. Where a callback’s result is tested — .filter(), .find(), .any(), .all() — only false and NUL are falsy, and everything else, including 0, "" and #[], is truthy.

print(#[0, "", false, NUL, #[]].filter(fn(x) { return x; })); // [0, "", []]

NUL is the absence of a value.

  • NUL == NUL is true; NUL equals nothing else (not 0, not false).
  • NUL cannot be ordered (NUL < 1) or used in arithmetic, except that "text" + NUL gives "textNUL".
  • A function without a return returns NUL. Iterators return NUL once exhausted, and dict.get(key) returns NUL for a missing key.
  • Reading a variable that was never defined is an error, not NUL.
  • NUL can be stored in arrays, dicts and tuples.

A note literal is a letter, an optional accidental (# or b), and an octave from -1 to 9. Notes map to MIDI 0–127 (C-1 to G9, C4 = 60); a note outside that range is a parse error. Notes compare with Numbers by MIDI value. A name like Cs2 is not a note; it parses as an identifier.

assert C4 == 60;
assert A4 >= 69;
assert Bb3 == A#3;
print(C-1); // the lowest MIDI note
print(G9); // the highest

Annotations are advisory: the language server uses them for signatures, hover and completion, and they are never checked at run time. A parameter annotated Number accepts any value.

WhereSyntax
Parameterfn f(x: Number)
Returnfn f() -> Number
UnionNumber | String
Function type(Number) -> Number
Class fieldlet speed: Number;
Tuple destructuringlet (f, l): (Number, String) = (440, "A4");
Variablelet x: Number = 1; — legal, but by convention left out
fn apply(f: (Number) -> Number, x: Number | String) -> Number {
return f(x);
}
print(apply(fn(n) { return n * 2; }, 5)); // 10

type(value) returns the type’s name as a String; for a class instance, the class name.

assert type(42) == "Number";
assert type("hi") == "String";
assert type(true) == "Boolean";
assert type([C4 D4]) == "Pattern";
assert type((1, 2)) == "Tuple";
print(type(#[1]));