Relations
Like concepts, relations are introduced by a vocabulary-defined metarel — there is no built-in rel keyword at lexer level. Stdlib ships a generic metarel std::core::rel<E1, E2>(E1, E2) for ontology-uncommitted relations; vocabulary packages can ship richer metarels (mediation, material, etc.) or re-export the generic.
// Same terminator rule as a concept (Vocabulary concepts and the generic `type` metatype): the `{ }` rel-body self-
// terminates; the bodyless form requires a ';' (`OE0011` if absent).
rel-decl ::= attribute* 'pub'? <metarel-name> Ident '(' rel-param-list ')'
cardinality-list? rel-supertype? ( rel-body | ';' )
rel-supertype ::= ('<:' | 'specializes') TypeExpr (',' TypeExpr)*
rel-param ::= Ident ':' TypeExpr
cardinality-list ::= cardinality+ // one slot per endpoint
cardinality ::= '[' range ']'
range ::= Nat '..' Nat | Nat '..' '*' | Nat '..=' Nat | Nat
// In cardinality position, `..` is INCLUSIVE on both ends
// (UML convention): `[1..1]` means exactly one, `[0..*]`
// means zero or more. Distinct from Rust range semantics.
rel-body ::= '{' field-list '}'
The leading identifier (after pub) is contextually a metarel-introducing keyword, resolved per Name resolution against the pub metarel declarations visible in scope — declared in this package or imported from a vocabulary package; rel is not ambient — the std::core baseline is brought in by use std::core::{rel} or a [package].prelude entry (RFD 0038). An unresolved introducer is refused with OE0606 UnknownMetarel. The parser disambiguates concept-decl (no (...) after the new name) from relation-decl (has (...)).
Three surface forms (using the std::core baseline, brought into scope via use std::core::{type, rel};):
use std::core::{type, rel};
// Form A — anonymous binary field on a concept
pub type Person { children: [Person] }
// Form B — named relation with optional navigation views
pub rel ParentOf(parent: Person, child: Person) [1..1] [0..*];
pub type Person {
parents: [Person] from ParentOf.parent,
children: [Person] from ParentOf.child,
}
// Form C — n-ary relation with intrinsic property body
pub rel Marriage(spouse_a: Person, spouse_b: Person) [0..1] [0..1] {
since: Date,
status: MarriageStatus,
}
pub rel Transaction(seller: Person, buyer: Person, item: Asset) {
amount: Money,
at: DateTime,
}
Cardinality (positional, UML association-end convention). For a relation with endpoints e₁, e₂, …, eₙ, the i-th cardinality slot bounds the number of distinct eᵢ for each fixed combination of all the other endpoints — the UML association-end multiplicity. (Equivalently: hold every endpoint but the i-th fixed, and the slot bounds how many distinct eᵢ may complete the tuple.) Omitted slots default to 0..*. For binary relations the slots read left-to-right; pub rel ParentOf(parent: Person, child: Person) [1..1] [0..*] reads “each child has 1..1 parents (slot 1 bounds the parents per fixed child); each parent has 0..* children (slot 2 bounds the children per fixed parent).” For n-ary relations the same per-position rule holds against the fixed combination of the others; pub rel Transaction(seller, buyer, item) [0..*] [0..*] [0..1] reads “for each (buyer, item) pair, 0..* sellers; for each (seller, item) pair, 0..* buyers; and for each (seller, buyer) pair, 0..1 items.”
Anonymous-field cardinality default. A field declared with the inline anonymous-relation form field: [T] (Form A) synthesizes a binary relation with default cardinality 0..* and Set semantics. To bound: field: [T; n..m]. To opt into ordered list semantics: field: [T; n..m, ordered] (lowers to List<T>). [T; <=1] emits OW2402 suggesting T? (Option<T>) instead.
Anonymous fields are structural shorthand: they do not engage the metarel calculus. No metarel name is attached; the synthesized relation is accessed only via dot-traversal. This is symmetric with struct/enum, which are likewise language-level structural and carry no metatype. Named relations (Forms B and C) require a metarel-introducing keyword in scope.
[T] on struct vs concept. On a struct or enum field, [T] is plain List<T> — no synthesized relation, no closure traversal, no metarel. On an ontologically-classified concept (declared under a metatype), [T] is Form A — a synthesized binary relation supporting closure traversal (alice.children+(b)). The difference is whether the enclosing declaration is data (struct/enum) or ontology (declared under a metatype — type or a vocabulary introducer).
When to upgrade Form A to Form B or C. Promote an anonymous field to a named relation when any of: (a) the relation needs to be classified under a metarel (mediation, material, …) so the substrate can reason over its properties; (b) the relation carries intrinsic data of its own (since: Date, amount: Money); or (c) the relation must be referenced as a first-class predicate from rule bodies (derive ancestor(d, a) :- ParentOf(d, a), …). When none of those apply, Form A is the right choice.
Generic relation-property characteristics. A named relation (rel or metarel) may carry the standard relation-algebra property characteristics (the OWL object-property characteristics) — PL/DB-neutral, describing the shape of the relation’s extent with enforcement, not an ontological commitment. All four are shipped by the std::rel standard-library package and must be brought into scope — use std::rel::{transitive, irreflexive, asymmetric, functional} (nothing is ambient — Name resolution / RFD 0038). #[transitive] is a genuine declarative macro (it expands to the closure derive). #[irreflexive] and #[asymmetric] are genuine procedural macros (RFD 0040): they re-emit the relation and paste the guarding check’s internal __{rel}_{prop} head via concat_idents. They read the relation’s name, so they apply to a rel; on a metarel they decline (the invocation is refused, OE0723). #[functional] is a builtin-backed macro (the #[rustc_builtin_macro] analogue, RFD 0037 D8) — the library owns the surface while a privileged compiler path implements it (a [0..1] cardinality cap on a rel’s target, or a guarding check on a metarel); re-emitting a rel with a modified cardinality bracket needs structural reflection. A characteristic applied without its import is refused — OE0705 for the macros #[transitive]/#[irreflexive]/#[asymmetric], OE1362 for the builtin #[functional]. Multiple characteristics compose on one declaration.
| Characteristic | Meaning | Enforcement |
|---|---|---|
#[transitive] (std::rel macro) | the relation is transitively closed | expands to a closure rule R(x, z) :- R(x, y), R(y, z) over the relation’s own extent, so the relation’s queryable extent includes its transitive closure |
#[irreflexive] | no element relates to itself | a check fires on any self-loop R(x, x) (OE1359) |
#[asymmetric] | R(x, y) forbids R(y, x) | a check fires on any mutual pair (OE1360); since a self-loop is its own converse, #[asymmetric] also forbids R(x, x) |
#[functional] | each source maps to at most one target | a max-cardinality [0..1] cap on the relation’s target position, refused at the write path (OE1341) — the same mechanism as a hand-written [0..1] bracket |
use std::rel::{transitive, irreflexive, asymmetric, functional};
#[transitive]
#[irreflexive]
#[asymmetric]
pub rel is_proper_part_of(part: Top, whole: Top);
#[functional]
pub rel inheres_in(burden: Aspect, bearer: ConcreteIndividual); // each aspect inheres in one bearer
Enforcement applies to the declared relation’s own extent. Propagation of a property borne by a metarel to the relations it classifies is out of scope (Out of scope). A characteristic applied without importing it from std::rel is refused — OE0705 for the macros #[transitive]/#[irreflexive]/#[asymmetric] (genuine macros, not directives), OE1362 for the builtin #[functional]. On a non-relation declaration (a concept, a rule), the still-directive #[functional] is refused with OE0707 (invalid position); the procedural #[irreflexive]/#[asymmetric] instead decline (the invocation is refused, OE0723), as they do on a metarel. (The ontology-specific relation properties — MLT’s #[partitions], #[categorizes], etc. — are vocabulary decorators and live in their packages, distinct from this generic family.)