Skip to content

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.

  • 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 modules, type bodies, and interfaces; calls look like User.rename(user) or Display.to_string(value), never value.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 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.

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.