Nomi Lang
Nomi is a statically typed, immutable language for writing small, expression-shaped programs that stay explicit as they grow. This site is both the language tour and the public reference: runnable examples execute in the browser, format themselves after valid edits, and show the same hovers as the editor.
Nomi at a glance
Section titled “Nomi at a glance”- Statically typed, with local inference — named functions spell out
parameters and every non-
Unitreturn type; bindings and lambdas infer. - Fully immutable — bindings, structs, and collections are all persistent; “modifying” returns a new value.
- Functional-first and pattern-matching — first-class functions, algebraic
data types, exhaustive
casedestructuring, and tail calls that run in constant stack space. - Clear local control flow —
return,break,continue, and prefixtryare available when they make a function easier to read. - Explicit interfaces — types implement interfaces with
impl Interface for Type { ... }, opt into generated implementations withderive, and call dispatch through qualified names. - Qualified calls, not value methods — behavior lives in modules, type
bodies, and interfaces; calls look like
User.rename(user)orDisplay.to_string(value), nevervalue.rename(). - Pipe-oriented —
x |> f()keeps data flow reading top-to-bottom. - Structured concurrency —
concurrent { ... }blocks, tasks, channels, and context-style cancellation keep spawned work scoped. - Typed app fields — runtime context, configuration, and dependencies live
on a concrete app value; tests can swap
dependencies with scoped
with MyApp.field = mockoverrides. - Built-in tests — named
test/testsblocks, assertions, setup, and//!attached examples are ordinary Nomi code run bynomi test.
Where Go fits
Section titled “Where Go fits”Nomi is implemented in Go: the interpreter, analyzer, LSP, stdlib
host functions, and runtime are Go code. Nomi does not compile to Go source.
The nomi CLI embeds the runtime directly, and the same interpreter, built to
wasm, runs the editable examples on this page.
Go is an implementation choice, not Nomi’s language identity. Nomi uses Go for garbage collection, cross-platform support, strong tooling, and practical runtime features while keeping its own syntax and semantics.
Most Nomi programs do not touch Go. It shows up when a package needs the
current dependency mechanics (go.mod next to nomi.toml) or when a package
wraps host functionality behind a typed Nomi API.
Concurrency borrows Go’s practical vocabulary — tasks, channels, context-style
cancellation — but task lifetime is structured: spawned work belongs to a
concurrent { ... } block and cannot leak out of it.
Coming from another language
Section titled “Coming from another language”Elixir — Pipes, pattern matching, immutable data, and
transformation-heavy functions transfer well. Expect static types, declared
ADTs instead of atoms/tagged tuples, Iter pipelines instead of
Enum/Stream, and structured concurrent { } blocks instead of actors.
Gleam — ADTs, Result/Maybe, no null, local inference, and
expression-shaped code should feel familiar. Nomi adds bare bindings,
explicit impl Interface for Type blocks, derive, app fields, resources,
typed literals, and pragmatic local control flow.
Rust — Traits map to interfaces, conformances use
impl Interface for Type { ... }, where bounds are checked at call sites,
Result/Maybe use prefix try, and distinct types fill the newtype role.
There is no ownership system, no mut, no macros, no value-dot methods, and
inference stays local to function bodies.
Go — You will recognize go.mod, go get, strong formatting, explicit
imports, channels, and context-style cancellation. The language model is not
Go’s: values are immutable, errors are Result/sum types, interface
conformance is explicit, and concurrency is structured.
Bindings & Expressions is the place to start; each chapter builds on the last.