64 Embedding
Motivating example
Suppose we have value types for Point and Circle:
value Point
{
Real x;
Real y;
}
value Circle
{
Point centre;
Real radius;
→ Real area = 3.14159 * radius * radius;
}
A wheel can be modelled as having the geometric properties of a circle, plus additional properties such as mass:
entity Wheel
{
embed Circle;
Real mass;
}
The declaration embed Circle; embeds a Circle value inside each Wheel. It also exposes the
members of Circle through Wheel, so a wheel has centre, radius, area, and mass
members.
This is an unnamed embedding. The embedded value is stored as part of the enclosing value, entity,
or quotient representation rather than referenced by identity. It is a member-declaration mechanism, not a subtype
declaration. Embedding Circle in Wheel does not make Wheel a subtype of Circle, and it
does not introduce implicit conversions between the two types. Top-level subtype embeddings use a
different declaration form, described in the subtyping chapters.
Forwarded members include both ordinary attributes and derived attributes. In the example above,
centre and radius are ordinary members of Circle, and area is a derived member of Circle;
all three are forwarded through Wheel. When a derived attribute is forwarded, its definition is
composed through the embedded value.
Named embedding
An embedding may also introduce a name for the embedded value:
entity Wheel
{
embed Circle as circle;
Real mass;
}
This introduces the named member circle : Circle and also forwards the members of Circle, so
circle, centre, radius, area, and mass are all available on Wheel.
For the named embedding above, the forwarded area is equivalent to circle.area.
If forwarded member names collide, unqualified lookup of those names is a static error. Named embeddings allow the model to keep the embedded values distinct and access their members through the embedding names.
Static rules
- An embedding declares a single mandatory embedded component. Cardinality qualifiers do not apply to embeddings. Optional or multi-valued containment should be modelled as an ordinary attribute.
- The embedded type must be a value-like nominal type, such as a value type, variant type, or enumerated type. Embedding an entity type is invalid; relationships to entities should be modelled using ordinary attributes so that identity and reference semantics remain explicit.
- Embedding does not introduce a subtype relationship and does not introduce implicit conversions between the enclosing type and the embedded type.
- An unnamed embedding forwards the members of the embedded type but does not introduce a member named after the embedded type.
- A named embedding introduces a member with the given name and also forwards the members of the embedded type.
- Forwarded members include both ordinary attributes and derived attributes of the embedded type. When a derived attribute is forwarded, its definition is composed through the embedded value.
- If several forwarded members, ordinary or derived, have the same name, unqualified lookup of that name is a static error. Named embeddings allow the members to remain accessible through the embedding names.
- A constructor must initialise each embedding exactly once, either by providing the named embedded value as a whole or, where this is unambiguous, by providing the forwarded members from which the embedded value is constructed. A constructor must not initialise the same embedding both ways.
- Forwarded members participate in lookup, predicate generation, and annotation in the same
way as ordinary members of the enclosing type. For embedded enums, the forwarded
attributes retain their type
{}on the enclosing type.
Embedding in quotient representations
Embedding is supported in quotient type declarations. In that context, an embedding contributes representation attributes to the quotient in the same way as ordinary representation fields. This is useful when a quotient representation naturally reuses an existing value-like structure.
For example, the numerator and denominator representation can be factored into a reusable value
type and embedded in Rational:
value RationalRepresentation
{
Integer numerator;
Integer denominator;
}
quotient Rational
{
embed RationalRepresentation;
equivalence
{
x ~ y where
x.numerator * y.denominator = y.numerator * x.denominator;
}
constraints
{
denominator > 0;
}
}
The unnamed embedding forwards numerator and denominator, so they are representation attributes
of Rational. They are available to the equivalence predicate, representative constraints,
derived attributes, and ordinary navigation exactly as directly declared representation fields
would be. The embedding does not make Rational a subtype of RationalRepresentation.
A named embedding may also be used when the embedded representation should remain addressable as a whole. Its forwarded members still participate in the quotient representation, subject to the ordinary collision and construction rules for embedding.
Embedding enumerated types
Enumerated types are formalised as variants whose attributes have type {}, the empty structural
type described in 47 Structural types. Embedding an enum therefore forwards those attributes
with the same type on the enclosing type:
enum Gender
{
male,
female
}
entity Person
{
String name;
embed Gender;
}
The embedded enum exposes male and female on Person. Since these paths have result type {},
they may be used directly in Boolean contexts, where they elaborate to existence tests. For example,
within the receiver context of a Person, male elaborates to male.Σexists.
The enum's variant constraint ensures that exactly one of its effective current base attributes holds for each person. Derived and dropped attributes do not participate in this constraint.