Lyric
A statically typed application language with a bytecode VM — standalone, and embeddable in a host with a capability-based sandbox.
What changed in v1.14.0 — 2026-08-19
The std polish, born from a line-by-line audit. The string module stops being quadratic, the
print family collapses to one generic concept, the collections learn the operations daily use
kept reaching for, arrays cross the native boundary as parameters for the first time — and two
new modules arrive: std.random and std.time. The bytecode format stays 3.2.
Added
print,println,eprintandeprintlntake anyDisplayvalue:println(42),println(true). The string forms keep working unchanged — a string displays as itself — andwrite/writelnwarn as deprecated; they were the same thing under a second name. Bytecode compiled before this release keeps running.Collections round out:
Listgainsinsert,removeAt,first,last,reverseandswap;MapgainsgetOr,clearandentries— key and value in ONE walk, without the second probe per key;Setgainsclearand isIterable, sofor (v in set)walks it directly.clearon all three keeps the backing for reuse; the values are released all the same.std.itergainsflatMap,chunks,reduceandfirst.Arrays cross the native boundary as parameters — the bytecode format always allowed it (§3 type grammar; format stays 3.2), the registry just never used it. On top of it:
std.io.file.writeBytesandappendBytes(the write side readBytes was waiting for),std.string.utf8Encodeandutf8Decode— the strict bridge: invalid bytes answernullinstead of the U+FFFD replacementreadTextdocuments — andfromCharsbecame one native call instead of one string per character.std.random: the generator moved out ofstd.math— randomness is not arithmetic — and gained what it was missing there:shuffle(Fisher–Yates over aList),choiceandnextGaussian. Deterministic, seeded by the caller, no capability. Thestd.math.Randomtwin stays one release as a deprecated migration path.std.time:InstantandDurationas value structs over epoch milliseconds —b.since(a),a.plus(d), andiso()rendering UTC ISO 8601 with floor semantics, so an instant before 1970 lands in the right day. Gated byosAccess, the same bit asstd.os: reading the clock is a question to the environment, and a new bit would be a contract change. The subtraction is a named method, not an operator —Instant - Instantyields a Duration, and the operator interfaces are homogeneous by the v1.13 decision.
Fixed
std.stringstops being quadratic.StringBuilder.buildandjoinfolded left and copied the whole result once per piece — both are one native join now;replacemoves untouched stretches as whole substrings; the searches, parsers and trims index a character array instead of calling O(n)charAtper position. Same results, different cost curve.Audit rests:
std.fmtloses its German locals,std.io.filea torn doc fragment, andstd.io.consolesorts a native above the "written in Lyric" divider it contradicted.