Skip to content
Nomi Lang Nomi Lang

A runnable introduction to the Nomi language.

Nomi is a statically typed, immutable language for writing 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.

  • Statically typed, with local inference — named functions spell out parameters and every non-Unit return 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 case destructuring, and tail calls that run in constant stack space.
  • Clear local control flowreturn, break, continue, and prefix try are available when they make a function easier to read.
  • Explicit interfaces — types implement interfaces with impl Interface for Type { ... }, opt into generated implementations with derive, and call dispatch through qualified names.
  • Qualified calls, not value methods — behavior lives in the nearest API container: usually a type owner, interface, or file API. Calls look like User.rename(user), String.length(name), or Display.to_string(value), never user.rename().
  • Pipe-orientedx |> f() keeps data flow reading top-to-bottom.
  • Structured concurrencyconcurrent { ... } 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 = mock overrides.
  • Built-in tests — named test / tests blocks, assertions, setup, and //! attached examples are ordinary Nomi code run by nomi test.

Nomi is implemented in Go: the interpreter, analyzer, LSP, stdlib host functions, and current runtime are Go code. Today, Nomi programs run through that runtime rather than compiling to Go source; the same interpreter, built to wasm, powers the editable examples on this page.

Go is an implementation choice, not Nomi’s language identity, but Nomi does follow Go’s lead on some practical toolchain and runtime choices: garbage collection, cross-platform support, strong tooling, dependency mechanics, and host integration. Nomi keeps its own syntax and semantics, and it does not copy Go’s package concept: a directory of files that forms a visibility unit.

Most Nomi code does not touch Go. Go shows up when a Nomi module uses the current dependency mechanics (go.mod next to nomi.toml) or when a module wraps host functionality behind a typed Nomi API. In Nomi prose, “module” is the Nomi unit rooted at nomi.toml; “Go package” means an importable Go package used at an FFI boundary, not a Nomi concept.

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 plus supervisors rather than actor mailboxes.

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, defer, 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. Nomi does not have Go’s packages. 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.