A small language; fun by default.

Ruka is an opinionated language focused on simplicity and useability, opt in to fine control when needed. Write what you mean; let the compiler fill in the rest.

Try it

Pick an example to run it in-browser.

OUTPUT
(no output)

The language, briefly.

A few features that shape how Ruka feels to write.

Inference-first types

Ruka is statically typed throughout. Type annotations are only required when inference needs a nudge β€” in practice, most code is annotation-free.

let name   = "Ruka"
let answer = 42
let lookup = (key: string) -> ?(int) do ...

Expression-based

if, match, loops and blocks all produce values. The same forms work everywhere an expression is expected.

let label = "pass" if score >= 60 else "fail"

let grade = match score with
    90..=100 do "A"
    80..=89  do "B"
    else        "C"
end

Opt-in control with modes

Five symbol prefixes cover mutability, ownership transfer, stack allocation, locality, and compile-time evaluation. They apply anywhere a name is introduced.

let @private = ..              // private / local
let consume  = (&buf) do ..    // takes ownership; mutable
let fast     = ($data) do ..   // copied to stack; mutable
let repeat   = (*n) do ..      // n is borrowed; mutable
let work = () do
    let counter = 0           // mutable by default at runtime scope
    let #lut = build_table()  // forced compile-time evaluation
end

Behaviours

A behaviour declares a set of method signatures. Any type whose methods cover those signatures satisfies it β€” no implements declaration needed. Dispatch is static, monomorphised at each call site.

let shape = behaviour {
    area(self):      () -> f64
    perimeter(self): () -> f64
}

let describe = (s: shape) do
    ruka.println("area=${s.area()}")
end

Patterns everywhere

Patterns destructure values and works in any declaration. Patterns slot directly into if, while, and for β€” no nested match required.

let (x, y)        = point   // tuple destructuring
let {name, age}   = person  // record destructuring

if some(name) = lookup(id) do
    ruka.println("hello, ${name}")
end

for ok(row) in rows do
    handle(row)    // rows that don't match ok(_) are skipped
end

Built-in testing

test declarations live alongside the code they test and can call local declarations directly. They are compiled out entirely in release builds β€” no runtime cost in production.

let @add = (a, b) do a + b

test addition = () do
    ruka.expect_eq(add(1, 2), 3)
    ruka.expect_eq(add(0, 0), 0)
end