Markdown for LLMs
Modeling vocabulary: entities, relations, enums, constants
The source Markdown for this article. Copy it into your assistant or download it as a text file.
# Modeling vocabulary: entities, relations, enums, constants
Vocabulary is the shared language a package speaks: the kinds of things it
talks about, the statements it can make, and the fixed values it reuses.
Rules, norms, and cases all sit on top of this layer, so getting the
vocabulary right is the first modeling step.
Everything on this page works with the public release of `law`, and no
input files are needed. Each claim carries a status in words
with a letter mark; the full legend is on the
[topic index](/corpus/#how-this-topic-marks-confidence). Every claim here is
**available in the public release** (S).
## The four constructs
Four declaration forms carry all vocabulary (**S**):
- `entity` declares a kind of thing, with `entity A : B` for subtyping.
- `relation` declares a predicate with typed parameters, plus `kind` and
`key` annotations where the model needs them.
- `enum` declares a closed set of named members.
- `const` declares a fixed value for reuse.
Visibility is explicit: `pub` marks what other packages may use,
`internal` keeps a declaration inside its package (**S**). The companion
page on [local and shared vocabulary](/corpus/vocabulary/local-and-shared/) explains when to choose each.
```law
entity Parcel;
entity Owner;
relation owns(owner: Owner, parcel: Parcel);
enum ParcelKind { residential, commercial, garden }
const registry_name : Text = "Parcel Registry";
```
The parcel example above is a trimmed retelling of the lab fixtures
that accompany this guide: package `labparcels.iface` contributes the
`Parcel` and `Owner` entities, the `owns` relation, and the
`ParcelKind` enum with exactly these three members, with no rules and
no sources — vocabulary only. The fixture spells each declaration in
full, adding labels, the `institutional` kind, and the key; the
[lab tour](/corpus/lab/) shows the complete text.
## What the engine checks
Declarations are not free text. The compiler rejects unknown types,
dangling references, and malformed keys with structured diagnostics, and
scenario runs exercise the vocabulary against facts (**S**):
- `law engine check` reports declaration problems before anything runs.
- `law test` runs a package's declared scenarios over its locked world.
- A key conflict at evaluation time keeps both values true and reports
`KEY_CONFLICT` rather than silently picking one (**S**).
## Which form to reach for
The four constructs above plus aliases, roles, definitions, and
mappings cover every modeling need. Pick by what you are saying, not
by how it reads in prose:
| Reach for | When | Not when |
|---|---|---|
| `type` alias | Same type under a shorter local name; the import stays pinned | Two dictionaries describe one idea — an alias cannot join them; import both and write an explicit mapping |
| New `entity` | A new kind of thing with its own identity, such as a parcel or an owner | Members of a closed set — the three parcel kinds are enum members, not entities |
| `role` | A capacity a subject acts in, shared across rules | A one-off rule variable or a parameter position — roles are shared vocabulary, not local shorthand |
| `relation` | A statement with parameters that a case submits or rules derive, with a kind saying which | A fixed reusable value — that is a `const`, not a relation |
| `definition` | The source states what a term means, as necessary conditions or an exact equivalence | An evaluative standard such as "reasonable", whose content arrives from case submissions or a named body — that is a relation fed through the judgment channel |
| Mapping | Correspondence between two existing vocabularies — see [bridges](/corpus/vocabulary/bridges/) | Both acts need the same concept — share one dictionary import instead of mapping two copies |
The forms in the table that are not among the four constructs, in one
line each:
- `type` alias — a second, local name for an existing type, for example
a short name for an imported dictionary type.
- `role` — a named capacity attached to an entity type, declared as
`pub role Servicer for Lender;`: the same lender can act as seller and
as servicer.
- `definition` — a declaration that states what a term means, either as
necessary conditions or as an exact equivalence; see
[Definition](/constructs/definition/).
- Mapping — an ordinary package of hand-written rules that relates
concepts of two vocabularies; see [bridges](/corpus/vocabulary/bridges/).
- Judgment channel — an `external judgment relation`: a decision input
whose value is supplied by a named authority, such as a court, instead
of being computed; see [Judgment channel](/constructs/judgment-channel/)
and the [contracts page](/corpus/vocabulary/contracts/#relation-kinds).
## Reproduction details
<details>
<summary>Toolchain, hashes, and full transcripts</summary>
All commands on the vocabulary pages were run against this toolchain,
observed 2026-10-03:
```
$ law version
law 0.1.1
semantics: law.core/0.2
std for language 0.2: 0.2.0
lawql: lawql/1 (queryResult 0.1)
binary hash: sha256:cfa7c17232f2dc594e665dbbf2ad3c154675797ef4be07ef7d758c9abfb80a28
```
</details>
## Where to go next
- The companion page on [local and shared vocabulary](/corpus/vocabulary/local-and-shared/) covers visibility
and shared dictionaries.
- The companion page on [stable identity](/corpus/vocabulary/identifiers/) explains how nodes keep their
identity across edits.
- The companion page on [contracts](/corpus/vocabulary/contracts/) covers relation kinds, keys, and
predicate templates.
- For a hands-on introduction, see [What we talk about](/tutorials/vocabulary/).
- For construct-level detail with corpus forms, see
[the Vocabulary construct reference](/constructs/vocabulary/).