Recipe-first functions
Every function is a recipe. It declares its dependencies,
produced value, failure modes, observability, and scaling properties — all
compiler-verified in committed mode (recipe!).
A programming language
Loom makes side effects, failure modes, and scaling properties first-class citizens of every function definition. The compiler verifies them statically, so runtime surprises die at build time.
// Classic recursion behind a refined domain. type NonNeg = Int where self >= 0 recipe fib(n: NonNeg) produces: Int { if n <= 1 { n } else { fib(n - 1) + fib(n - 2) } }
$ loom exec examples/13_fibonacci.loom fib 20 [1/3] Loom → Rust ... OK (127 lines) [2/3] Rust → binary ... OK 6765 [3/3] Execute ....... exit code 0
Why Loom
Six pillars hold Loom up. Each one is enforced by the compiler, not documented in comments.
Every function is a recipe. It declares its dependencies,
produced value, failure modes, observability, and scaling properties — all
compiler-verified in committed mode (recipe!).
Tighten a base type with a pure predicate: type Port = Int where
self in 1..65535. Narrowing inserts a runtime check at the dispatcher
boundary. Widening is always safe.
Every recipe carries a row of effects: IO, Async,
Fail<E>, Mut<S>, Rand,
Time. Row mismatches are compile errors, not surprises.
One .loom source compiles to Rust, TypeScript, Go, or
WebAssembly Text. Refinement validation runs at the dispatcher boundary
in every target. Generated code is zero-dependency.
spawn, race, all, and typed
LoomChannel<T> with send/recv.
The effect row tracks Async automatically.
loom exec drives Loom → Rust → rustc → run with
a content-addressed FNV-1a cache. Warm runs reuse the compiled binary — ~4×
speedup on hits. No cargo required.
The exec pipeline
loom exec <file.loom> [recipe] [args...] parses, type-checks,
verifies effects, emits self-contained Rust, compiles it with rustc,
caches the binary, and runs it. Every stage prints its status.
Lexer, parser, bidirectional type checker, effect verifier, and Rust codegen.
Emits a self-contained .rs file with an embedded stdlib.
OK (127 lines)
Shells out to rustc --edition 2021. The generated code has
zero Rust-side dependencies, so no cargo project setup is needed.
cached (b98fe747)
Runs the binary with stdin/stdout/stderr inherited. Refinement violations surface verbatim; the child's exit code propagates.
exit code 0
Tip Click any stage to highlight it, or jump to the quickstart to try it yourself.
One source, four targets
loom build --target=<rust|typescript|go|wasm> file.loom
emits idiomatic source for any of the four backends. Refinement validation
happens at the dispatcher boundary in every target.
pub fn factorial(n: i64) -> Result<i64, LoomError> { if n <= 1 { Ok(1) } else { Ok(n * factorial(n - 1)?) } }
Runs with rustc --edition 2021. No
dependencies. No Cargo.toml. Self-contained stdlib inlined.
export function factorial(n: number): number { if (n <= 1) { return 1; } else { return n * factorial(n - 1); } }
Runs on Node with the --experimental-strip-types
flag. Integer divisions become Math.trunc() to match Loom semantics.
func factorial(n int64) int64 { if n <= 1 { return 1 } return n * factorial(n - 1) }
Runs with the standard go run toolchain.
Concurrency lowers spawn to go func() with typed channels.
(module (func $factorial (param $n i64) (result i64) (if (result i64) (i64.le_s (local.get $n) (i64.const 1)) (then (i64.const 1)) (else (i64.mul (local.get $n) (call $factorial (i64.sub (local.get $n) (i64.const 1))))))) (export "factorial" (func $factorial)))
The arithmetic-and-control-flow subset. Runs in any
WASM runtime — wasmtime, wabt, or V8 via WebAssembly.instantiate().
Recent examples
Each of these ships in the repo under examples/ and runs
end-to-end through loom exec. Click a card to expand.
Composition of println, toString, abs,
max, and min — the core stdlib surface that every
backend inlines.
recipe clamped(value: Int) produces: Int { min(max(value, 0), 100) }
$ loom exec examples/12_builtins.loom clamped 250 → 100
Classic recursive algorithms (fib, fibIter,
gcd, power) behind a refined NonNeg
domain. The dispatcher rejects negative input before the body runs.
type NonNeg = Int where self >= 0 recipe gcd(a: NonNeg, b: NonNeg) produces: Int { if b == 0 { a } else { gcd(b, a - b * (a / b)) } }
$ loom exec examples/13_fibonacci.loom gcd 48 36 → 12
Producer → transformer → consumer using typed LoomChannels.
Helper recipes keep each spawn block to a single expression so the generated
Rust stays readable.
recipe pipeline(n: Int) produces: Int { let source = channel() let squared = channel() let p = spawn { send(source, n) } let t = spawn { send(squared, square(recv(source))) } recv(squared) }
$ loom exec examples/14_pipeline.loom pipeline 5 → 25
Multiple refined Int types (Score, Age) describe
different business domains. Each gets its own runtime constructor.
type Score = Int where self in 0..101 type Age = Int where self in 0..151 recipe grade(a: Score, b: Score, c: Score, d: Score) produces: Int { (a + b + c + d) / 4 }
$ loom exec examples/15_refinements.loom grade 85 90 78 92 → 86
A three-level import chain shows the module loader's diamond-dedup behavior.
analytics.loom imports geometry.loom which imports
math.loom. The shared leaf compiles once.
// analytics.loom import "./geometry.loom" import "./math.loom" recipe hypotSq(a: Int, b: Int) produces: Int { square(a) + square(b) }
$ loom exec examples/modules/analytics.loom hypotSq 3 4 → 25
Quickstart
cargo install --lock loom-lang
The crate is loom-lang (name taken on crates.io), but the
installed binary is simply loom. The --lock flag
respects the published Cargo.lock for reproducible installs.
loom exec examples/13_fibonacci.loom fib 20 # [1/3] Loom → Rust ... OK (127 lines) # [2/3] Rust → binary ... OK # 6765 # [3/3] Execute ....... exit code 0
Prefer to hack on the compiler itself? Clone the repo and run
cargo build --release — the dev binary lives at
target/release/loom.
type Port = Int where self in 1..65535 recipe validatePort(port: Port) produces: Int { port }
$ loom exec hello.loom validatePort 8080 # → 8080 $ loom exec hello.loom validatePort 0 # → RefinementViolation
loom check <file> — type-check and verify effects onlyloom build --target=typescript <file> — emit TypeScriptloom test <file> — run every test_* recipeloom fmt <file> — canonical format (idempotent)loom lsp — start the language server on stdioloom cache info — inspect the compile cacheRead the deep dive on the loom exec pipeline,
or skim the LLM contextualization file to onboard an AI collaborator.