Skip to content

Torvik v1.3.0 — Ravens & Bridges

Choose a tag to compare

@torvik-lang torvik-lang released this 19 Jul 00:03
· 92 commits to main since this release
Immutable release. Only release title and notes can be modified.
fece543

The concurrency release. Torvik programs can now run work on multiple threads — designed so data races aren't something you avoid, they're something the language gives you no way to write.

🐦‍⬛ Ravens: concurrent tasks

raven spawns any df function on its own OS thread — fire-and-forget, or bind a task<T> handle and collect the result with join:

set h: task<str> = raven fetch(url);
// ...other work...
fixed page: str = join(h);
  • Arguments deep-copy at the spawn, so a task owns its own copy of everything it receives — after the spawn line, the two threads share nothing.
  • Spawned functions must be self-contained (no globals, transitively) — enforced at compile time with an error naming the function and the global. This is what keeps the reference-counting runtime lock-free.
  • Every crossable type works: all integer widths, f64, bool, str, i128/u128, and aett values.
  • Each handle joins exactly once; a panic in a task stops the whole process immediately — background failures are never silently swallowed.

🌉 Bridges: typed channels between tasks

A bridge<T> is a buffered, thread-safe queue — the one object tasks share, protected by its own lock:

df produce(ch: bridge<i64>, n: i64) -> void {
    set i: i64 = 0;
    whilst i < n { send(ch, i); i += 1; }
    bridge_close(ch);
}

df main() -> void {
    set ch: bridge<i64> = bridge_new(4);
    raven produce(ch, 10);
    set total: i64 = 0;
    whilst true {
        set r: result<i64> = try_recv(ch);
        check is_err(r) { break; }
        total += unwrap(r);
    }
    echo!("sum: {total}");
}
  • Values deep-copy on send; recv blocks while empty; bridge_close is idempotent and wakes everyone.
  • try_recv returns a result<T> that goes err exactly once the bridge is closed and drained — the clean end for a worker loop.
  • Multiple producers and consumers on one bridge are fully supported.
  • A bridge's own refcount is the single atomic operation in the entire design; every other refcount in the language stays lock-free.

🧵 Strings are data; interpolation belongs to echo and fmt

String interpolation is now scoped to echo, echo!, and fmt — a string literal written directly as one of their arguments interpolates exactly as before ({name}, expressions, {{ escapes). A string literal anywhere else is plain data:

fixed css: str = ".a{color:red}";     // just characters — compiles fine now
fixed slot: str = "{{title}}";        // exactly {{title}}, no escapes needed
fixed msg: str = fmt("Hi {name}");    // fmt still interpolates

Previously every literal interpolated, so brace-heavy data — CSS, JSON, HTML templates — needed {{ escapes everywhere and could trip "undefined variable" errors on ordinary text. Code that kept interpolated literals inside echo/fmt (the documented idiom all along) is unaffected.

🔤 Variadic str_concat

str_concat(a, b, c, ...) now folds any number of arguments left-to-right — no more nesting. Calling it with one argument is a clean compile error; previously a fixed two-argument form silently dropped extra arguments, which could hide real bugs.

📚 Standard library v1.2.0

Additive growth, no breaking changes:

  • std::convert (new module) — to_hex, to_bin, from_hex, to_int (parsers return result<i64>, so bad input is recoverable)
  • std::strings — strip_prefix, strip_suffix, is_digits, is_alpha, center, truncate
  • std::list — contains_int, contains_str_in, count_int, unique, take, drop, mean
  • std::math — pow_checked (overflow-signalling power), digit_count, at_least, at_most, ilog2

Projects can require the new floor with std = "1.2.0" in torvik.rune.

🗂 Table iteration + a memory-safety fix

  • table_keys(t) returns every key as a sorted list<str> (sorted so loops over a table are reproducible — hash order shifts as a table resizes). Iterate the keys, fetch values with table_get.
  • Fixed: functions returning a table or bag handed back freed memory (a missing +1-on-return retain). fixed cfg: table<str, str> = read_config(path); now works exactly as you'd expect.

🔧 Also in this release

  • File-system surface for real tools: dir_list, fs_is_dir, binary-safe fs_copy, recoverable I/O via try_writefile / try_appendline / try_fs_copy, and a new unused_result warning category.
  • The lexer's two keyword lists are now one — is_keyword derives from keyword_for, eliminating an entire drift-bug class.
  • Installer hardening: std modules are now derived from std.tv itself, so a new module can never be silently skipped again; the Windows installer also gained the compiler-lib sources it was missing.
  • Test suite grown from 100 to 123 cases, including deterministic concurrency tests (no sleeps — ordering is forced by joins and bridge blocking).

📖 Documentation

The Language Guide has a full new chapter — Concurrency: ravens and bridges — covering the ownership model, the self-contained rule and its why, and the worker-loop pattern. STDLIB.md, the Roadmap, and the wiki are all updated.

Install

Linux

curl -fsSL https://raw.githubusercontent.com/torvik-lang/torvik/main/linux/install.sh | sh

Windows (PowerShell)

iwr -useb https://raw.githubusercontent.com/torvik-lang/torvik/main/windows/install.ps1 | iex

Already installed? rune update brings you to v1.3.0.

Full details in the CHANGELOG. Binaries for Linux x86-64 and Windows x86-64 are attached below.

Self-hosting, compiled to native via LLVM. No VM, no GC — automatic reference counting, now lock-free across threads by design.