47 Structural types
A structural type is a type whose identity is determined by the shape written in its type expression, rather than by a nominal declaration. Structural types are written as an ordered sequence of typed attributes:
{ Real x; Real y; }
{ Point centre; Real radius; }
{ Supplier supplier*; Part part*; Shipment shipment*; }
Structural types are also called tuple types. Formally, a structural value records an extension for each attribute. The extension of an attribute is a set of values of the attribute's declared type, even when the attribute has exactly-one cardinality.
Carrier sets of structural types
A structural type is an ordered sequence of attribute declarations. Each attribute declaration has an attribute name, an attribute type, and a cardinality constraint.
Let S be a structural type with declarations:
S = ((A1, T1, C1), ..., (An, Tn, Cn))
where Ai is an attribute name, Ti is a type, and Ci is a cardinality constraint. Attribute order is part of the structural type. Attribute names are available for selection, but the carrier is defined from the ordered declarations.
For each attribute declaration, a value of the structural type assigns an extension to the attribute. The extension is a subset of the carrier of the attribute type, and its cardinality must satisfy the declared cardinality constraint.
carrier(S) =
{
((A1, E1), ..., (An, En))
|
Ei ⊆ carrier(Ti)
∧ Ci(|Ei|)
for each i ∈ {1, ..., n}
}
Thus a structural value is a tuple of attribute extensions. It is not obtained by multiplying those extensions out into a Cartesian product.
For example:
{
Integer a;
Real b*;
}
has carrier:
{
((a, A), (b, B))
|
A ⊆ Integer
∧ |A| = 1
∧ B ⊆ Real
}
The extension of a must contain exactly one integer. The extension of b
may contain any number of real values, including none.
This matters when an extension is empty. For example, the values:
((a, {3}), (b, ∅))
((a, {4}), (b, ∅))
are distinct.
Structural identity
Two structural type expressions denote the same type exactly when they have the same ordered sequence of attribute names, cardinality qualifiers, and attribute types.
type Point2D = { Real x; Real y; };
type AlsoPoint2D = { Real x; Real y; };
type SwappedPoint2D = { Real y; Real x; };
Point2D and AlsoPoint2D are names for the same structural type. SwappedPoint2D denotes a
different structural type because the attribute order is different. Attribute order is part of
structural type identity so that ABIs, serialisation formats, and other external representations
can rely on a stable positional layout.
Structural type equality is stricter than structural subtyping. A type may be structurally assignable or embeddable without being the same structural type. The subtype chapters define the subtyping rules.
Empty and nested structural types
The empty structural type is written {}. It has no attributes and is useful when a type position
requires a payload but the payload carries no data, for example in variant attributes.
variant Option<T>
{
T some;
{} absent;
}
Structural types may be nested. For example, a single structural type can describe the shape of a small supplier-parts database:
{
{
Integer id;
String name;
Integer status;
String city;
} supplier*;
{
Integer id;
String name;
Color color;
Real weight;
String city;
} part*;
{
Integer supplier_id;
Integer part_id;
Integer qty;
} shipment*
}
Deeply nested structural types are usually not the best way to model domain concepts. Nominal types are usually preferable when the shape has an intended meaning, is reused, or is expected to evolve.
Structural values
A structural value may be created with a named-field structural literal when the expected type is structural:
{ Real x; Real y; } point = { x = 1, y = 3 };
Each required field must be initialised exactly once. Each initializer is checked against the corresponding field type. The literal matches fields by name; the structural type still records the declared field order as part of its identity.