Skip to main content

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 supportedWhy
In-place mutation or mutable stateValues are assigned once and never change; provider-backed writes use explicit std::Mutation plans instead
RecursionAll computation must terminate statically
Unbounded loopswhile-style control flow does not exist
Hidden side effectsEvery external read and provider write is explicit, declared, and host-admitted
Turing-completenessThe 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 variablesEnv<T> {} reads named variables from the host environment once per compilation
  • FilesFile::Read<T> { path, format } reads a local file at a statically-known path, including dotenv files with the same flat text Decode<...> support as Env<T> {}
  • HTTP fetchesHTTP::Fetch<T> { ... } makes a network request with explicit cache/pin semantics
  • Providers — out-of-process extensions registered in rank.toml that 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 descriptorEmit::manifest(...) returns std::Emit<T> for later materialization
  • A commit-aware descriptorMutation::commit(...) returns std::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

ToolRelationship
JsonnetSimilar scope; Rank adds a structural type system and explicit external inputs
CUESimilar type model; Rank is evaluation-first rather than constraint-unification-first
DhallSimilar determinism goal; Rank is array-oriented and supports providers
Helm / KustomizeRank replaces the templating layer with a typed, validated language
General scripting (Python, Bash)Rank handles the data-generation step; scripting handles deployment and orchestration