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.