Concepts
Rank is a declarative, deterministic, finite configuration language for generating structured outputs and explicit emit descriptors.
Understanding what Rank deliberately is not is just as important as understanding what it is.
Read this page when you are deciding whether Rank fits typed configuration, provider-backed workflows, or HTTP handlers with explicit external inputs. For runnable programs, browse Examples. For commands, see the CLI reference. For server and provider surfaces, jump to HTTP Server, AWS Provider, Faker Provider, and Provider authoring.
What Rank is
Rank is a functional, graph-evaluated language for producing structured data and explicit emit descriptors. You write a program that describes an output — a configuration file, a dataset, a manifest, or a commit-aware result — and Rank evaluates it. When a program returns a top-level std::Emit<T> descriptor, the host decides how to realize it.
Rank is well-suited for:
- generating configuration files (Kubernetes manifests, Terraform inputs, CI pipelines)
- producing typed, validated JSON or YAML from a structured description
- transforming and combining data from external sources in a reproducible way
- modeling explicit provider-backed writes without hiding when commit occurs
- enforcing invariants (uniqueness, port ranges, cross-field constraints) at compile time
What Rank is not
Rank is not a general-purpose programming language. The following are intentional non-goals:
| Not supported | Why |
|---|---|
| In-place mutation or mutable state | Values are assigned once and never change; provider-backed writes use explicit std::Mutation plans instead |
| Recursion | All computation must terminate statically |
| Unbounded loops | while-style control flow does not exist |
| Hidden side effects | Every external read and provider write is explicit, declared, and host-admitted |
| Turing-completeness | The compiler can fully analyze any valid program |
Rank is not a general-purpose application runtime. It produces data — configuration files, datasets, manifests, and explicit emit descriptors — that other systems consume. The rank serve command is the main exception: it provides a lightweight HTTP server for programs whose serve-time pub main accepts Runtime::ExecutionContext<Routes> and returns an HTTP::Response, but the handler logic itself is still a declarative Rank function evaluated per request.
Core concepts
Immutable bindings
Every name in a Rank program is assigned exactly once. There is no reassignment, no mutation, and no mutable state:
host = `api.example.com`
port = 8080
// host and port cannot be changed after this point
This makes the value of any binding trivially traceable — it is always the expression on the right-hand side of its declaration.
Graph evaluation
Rank builds a directed acyclic graph (DAG) of bindings before evaluating anything. Nodes are bindings and expressions; edges are data dependencies. The compiler evaluates nodes in topological order — each value is computed exactly once, after all its inputs are ready.
Cycles are compile errors. If a depends on b and b depends on a, the program is rejected before evaluation begins.
width = 1920
height = 1080
aspect_ratio = width / height // evaluated after width and height
Arrows point from dependency to dependent. width and height have no incoming edges — they are evaluated first. aspect_ratio is evaluated last, once both inputs are ready.
Determinism
Given the same source, the same declared external inputs, and the same compiler version, ordinary Rank evaluation and emit descriptor construction always produce the same values. There is no ambient randomness, no hidden clock access, and no implicit network IO during ordinary compute.
When a host realizes Mutation::commit(...), possible write outcomes are still explicit in typed commit results rather than hidden inside ordinary evaluation.
This makes Rank programs safe to commit, cache, and reproduce.
Explicit external inputs
Rank can read from the outside world, but every external dependency must be explicitly declared. The supported external input forms are:
- Environment variables —
Env<T> {}reads named variables from the host environment once per compilation - Files —
File::Read<T> { path, format }reads a local file at a statically-known path, includingdotenvfiles with the same flat textDecode<...>support asEnv<T> {} - HTTP fetches —
HTTP::Fetch<T> { ... }makes a network request with explicit cache/pin semantics - Providers — out-of-process extensions registered in
rank.tomlthat expose typed reads and writes through a namespace
Because every external dependency is declared and every provider write crosses an explicit commit boundary, the compiler can list all dependencies (rank deps), and repeated runs with cached inputs remain reproducible.
Data providers
Providers are out-of-process extensions that expose typed operations to Rank programs. They can expose pure helper functions, backend-style reads, and explicit mutation exports. @rank-lang/plugin-faker and @rank-lang/plugin-aws are first-party examples.
Providers are the only way to introduce capabilities beyond pure data transformation — and even then, the capability (network, for example) must be explicitly declared in the provider manifest and admitted by the host.
Providers are not plugins to the compiler. They are separate processes that the compiler spawns and communicates with over a JSON-line stdio protocol. A Rank program that uses a provider is still fully type-checked and deterministic with respect to its declared inputs.
Explicit mutation boundary
Rank does support external writes, but not as mutable state inside the language.
- ordinary bindings stay immutable
- provider-backed writes are declared as
Mutation::Effect<T>obligations - fulfilled mutation values may feed later mutation payloads, but they stay commit-local
- actual external IO happens only when a host realizes a top-level
Mutation::commit(...)
That split is what lets Rank support writes without turning ordinary evaluation into imperative execution. See Mutations for the full lifecycle.
The type system
Rank has a structural type system. Types describe the shape of values, not their identity. Object types are closed by default:
Config = Object {
host: string,
port: number,
}
The language supports generics, including declaration-site constraints (K extends keyof T) and trailing defaults (Box<T = string>), plus type unions (A | B), type intersections (A & B), optional fields (field?: T), and open object types (...: T for additional fields). Type aliases use leading-uppercase names.
Structured text is a first-class part of the language too: parse template types let programs treat string formats as types, and the same parse / pattern syntax reappears in match expressions and route matching instead of pushing string handling out into ad-hoc helper code.
Compile-time constraints
Annotations attach compile-time invariants to declarations and to ordinary object-type fields:
@constraint(cond: |self| self >= 1 && self <= 65535)
api_port = 8080
@unique(group: ports)
api_port = 8080
@unique(group: ports)
db_port = 5432
ServiceConfig = Object {
@constraint(cond: |self| self >= 1)
replicas: number,
@sensitive
token?: string,
}
Constraint failures are compile errors, not runtime errors. Declaration annotations validate a specific binding; field annotations travel with the type and are enforced wherever that type is checked.
Output model
For ordinary command-line evaluation, the entrypoint is the exported pub main function. Its return value is what the host emits or realizes.
Rank can emit:
- One ordinary document — bare
rank <entry>prints the evaluated document result as YAML by default, or as JSON with--format json - A multi-file manifest descriptor —
Emit::manifest(...)returnsstd::Emit<T>for later materialization - A commit-aware descriptor —
Mutation::commit(...)returnsstd::Emit<T>with typed write outcomes projected into ordinary output
Emitted output preserves evaluated field order. There is no implicit reordering.
See Output and manifests for the std::Emit<T> manifest workflow and --file-root behavior, and Mutations for commit-aware output.
How Rank relates to other tools
| Tool | Relationship |
|---|---|
| Jsonnet | Similar scope; Rank adds a structural type system and explicit external inputs |
| CUE | Similar type model; Rank is evaluation-first rather than constraint-unification-first |
| Dhall | Similar determinism goal; Rank is array-oriented and supports providers |
| Helm / Kustomize | Rank replaces the templating layer with a typed, validated language |
| General scripting (Python, Bash) | Rank handles the data-generation step; scripting handles deployment and orchestration |