Torvik

A tour of Torvik

Everything below is real, compiling Torvik. This is the quick tasting menu — for the full story, start with the Language Guide, which teaches the whole language from the beginning.

Variables

set declares a variable; fixed declares one that cannot be reassigned.

set count: i64 = 3;
fixed name: str = "Odin";
count += 1;

Functions

Functions are declared with df. Every parameter and return type is explicit.

df square(n: i64) -> i64 {
    return n * n;
}

A parameter marked ^ is optional (callers may omit it, and it takes a type-appropriate zero); a trailing * parameter is variadic, gathering the remaining arguments into a list. Every argument is type-checked at the call site.

df greet(name: str, ^greeting: str) -> void {
    check greeting == "" { greeting = "Hail"; }
    echo!("{greeting}, {name}!");
}

df loudest(*words: str) -> str {
    set best: str = "";
    each w in words { check len(w) > len(best) { best = w; } }
    return best;
}

greet("Astrid");            // Hail, Astrid!
greet("Bjorn", "Well met"); // Well met, Bjorn!

Printing and interpolation

echo! prints with a newline and interpolates {name} directly - variables or full expressions. fmt builds a string value the same way, and also takes positional {} placeholders.

echo!("Hello {name}!");
echo!("total: {price * qty}");
fixed line: str = fmt("{} + {} = {}", 2, 3, 5);

Interpolation belongs to echo, echo!, and fmt alone. A string literal anywhere else is plain data - braces are just characters, so CSS, JSON, or template text needs no escapes:

fixed css: str = ".rune{color:red}";
fixed slot: str = "Tour";

Conditionals and guards

check is Torvik's if; fallback is its else. guard states what must hold and handles the exit path up front.

check n > 9 {
    echo!("big");
} fallback {
    echo!("small");
}

guard len(name) > 0 fallback {
    echo!("a name is required");
    return;
}

Aetts and pattern matching

An aett - named for the rune families - is an enumeration. when matches over it, and the compiler checks every variant is covered.

aett Status { Draft, Published, Archived }

df describe(s: Status) -> str {
    set out: str = "";
    when s {
        Status::Draft     => { out = "still being carved"; }
        Status::Published => { out = "raised for all to read"; }
        Status::Archived  => { out = "weathered but standing"; }
    }
    return out;
}

Loops

whilst loops on a condition; each walks a range or a list.

each i in 0..3 { echo!("run {i}"); }

fixed runes: list<str> = split("fehu,uruz,thurisaz", ",");
each r in runes { echo!(r); }

The weave

The weave operator ~> threads a value through a pipeline, left to right:

fixed slug: str = " Hello, Torvik " ~> trim ~> lower ~> replace(" ", "-");
// hello,-torvik

Errors as values

Fallible operations return a result - checked explicitly, no exceptions:

set r: result<str> = try_readfile("saga.txt");
check is_err(r) {
    fixed why: str = err_msg(r);
    echo!("could not read: {why}");
    return;
}
fixed text: str = unwrap(r);

Concurrency: ravens and bridges

raven spawns a function on its own OS thread; a bridge is a typed, buffered channel between tasks. Arguments deep-copy at the spawn and values deep-copy on send, so threads share no mutable state - and the compiler enforces that spawned functions touch no globals.

df fetch_len(path: str) -> i64 {
    return len(readfile(path));
}

df main() -> void {
    set h: task<i64> = raven fetch_len("saga.txt");
    // ... other work ...
    fixed n: i64 = join(h);
    echo!("{n} bytes");
}

Collections

set xs: list<i64> = list_new();
push(xs, 7);

set cfg: table<str, str> = table_new();
table_set(cfg, "title", "My Site");
fixed keys: list<str> = table_keys(cfg);   // sorted, so loops are reproducible

Ready to learn it properly? Start the Language Guide — it goes from no programming experience at all through to concurrency and shipping real projects.

Or install Torvik and rune new your first project, and see how far the language goes in Vefna, a real tool built with it.