Skip to content

What is PGDL? ​

PGDL (Phony Generator Definition Language) is the declarative, JSON-based language for defining generators — pure producers of values. You don't write how to produce a value step by step; you declare what it is (an id; a name that sounds Turkish; an email built from that name) and Phony produces it, reproducibly, from a seed.

  • Declarative — describe the value, not the procedure.
  • JSON — editable by hand, by tools, and by agents; diffable in Git.
  • Deterministic — the definition, a seed, an invocation index, and the reference instant fully determine the output (see How generation works).

A library language, not a dataset spec ​

The sharpest way to place PGDL: it's a language for building a library of generators, not a language for describing a dataset. A dataset-spec language would talk about tables, how many rows each has, which column is a foreign key into which other, and how two tables stay consistent. PGDL says none of that. It only ever answers "what is one value, and how is it produced?" — reproducibly, from a seed.

That single-minded scope is a deliberate design choice, not a gap. Because a generator never mentions a table or a sibling column, it stays pure and portable: the same @acme/person:full_name behaves identically whether it's invoked by the CLI, an OSS library, or the cloud product binding it to a live database's schema. The moment a generator could reach a table, it would stop being reusable. Wiring generators into tables and relationships is a different layer — the cloud product (see What PGDL is not).

A generator is just a generator ​

A generator knows nothing about tables, columns, rows, or relationships. It takes a seed (plus a few ambient inputs) and produces one value. That value can be a single scalar, or a structured object or array — even nested — and its keys may be named or not. How many values you want is a separate concern (see Generation); arranging values into tables is a different layer entirely — the cloud product (see What PGDL is not).

The shape of a generator ​

Every generator — built-in or custom — is one uniform envelope:

json
{ "use": "@phony/core:logic.int_between",
  "params": { "min": 18, "max": 80 },
  "constraints": { },
  "modifiers": { "unique": true, "nullable": 0.05, "transform": "<PEL>" } }

In PGDL you usually write the friendly per-type shape, which compiles to that envelope:

json
{ "type": "logic", "algorithm": "int_between", "params": { "min": 18, "max": 80 } }

See Generators in depth for the envelope, the universal modifiers, and every generator type.

Generators are made of generators ​

A generator can be built from other generators — and those from others, nested without limit. The pure-data way is a composition: { name, params, let, body }, where each let step runs a generator (or computes a PEL expression) and body assembles the result.

json
{
  "name": "@acme/person:full_name",
  "let": {
    "first": { "type": "model", "source": "person.first_names", "generation": { "mode": "word" } },
    "last":  { "type": "model", "source": "person.last_names",  "generation": { "mode": "word" } }
  },
  "body": "{{ capitalize(first) }} {{ capitalize(last) }}"
}

References inside a generator resolve against its own let/param values — never a table or a sibling field. To reuse one value, name it with a let and reference that name; the same name yields the same value (coherence is a property of the let). A composition is usable exactly like a built-in once registered, so generators ship in packages — no Rust required.

Generation: the invocation side ​

A generator's definition is one thing; generation — calling it — is another. Invoking a generator is like sampling an n-gram model: you ask for one value, or for N. Each invocation gets its own derived seed and an ordinal index (what sequence/cycle key off); the unique and nullable modifiers apply across a run of invocations.

rust
let value  = generate_one(&def, &registry, seed, 0)?;   // one value
let values = generate(&def, &registry, seed, 1000)?;    // a thousand

See The engine (Rust) for the runnable API.

What PGDL is not — and never will be ​

PGDL defines generators, full stop. Tables, foreign keys, relationships between tables, row-level consistency, "how many rows of each entity," and which data a generator transforms (entities, relationships, scenarios, sync rules) are not PGDL — not "not yet," but a different layer: the Phony cloud / SaaS product, which decides how generators are wired together and applied to real data. Keeping PGDL to only generator definition is what lets a generator stay pure and portable — the cloud then binds the same generators to an authored schema or to one introspected from a live database. See the cloud platform spec for that layer.

How to read the rest of these docs ​

You want…Read
The envelope, modifiers, every generator typeGenerators in depth
Real input → real output for every featureGenerator gallery
The {{ … }} expression language and its functionsExpressions (PEL)
Composition, seeds, determinism, invoking a generatorHow generation works
Install & call a generator (Rust)The engine (Rust)

The exhaustive design specification lives under Spec → PGDL Specification; the table/relationship/sync layer is in the Cloud platform spec.

Phony Cloud — Documentation & Specification