§2.7 Advanced structures

This section discusses how role containment and the playedBy relationship can be combined. It does not define new rules, but illustrates rules defined above. The central idea is that any class can have more than one of the three flavors team, role, and base.

(a) Nesting

If a role (contained in a team) is also a team (marked with the team modifier) it is a nested team. The depth of nesting is not restricted.

(b) Stacking

If the base class to which a role is bound using playedBy is a team, the role is said to be stacked on the base team.

(c) Layering

If roles of a team Secondary are played by roles of another team Primary (i.e., base classes are roles), the team Secondary defines a layer over the team Primary. Such layering requires a final reference anchor from Secondary to an instance of Primary. All playedBy declarations within Secondary specify their base classes anchored to that final link anchor.

Team layering example

Due to the anchored base types, layered teams implicitly support the following guarantee: all base objects of roles of Secondary are contained within the team instance specified by the link anchor. If roles of Secondary contain any callin bindings to non-static base methods, these will be triggered only when a base method is invoked on a base instance contained in the team specified by anchor.
In accordance with §2.6.(a) the anchor in such anchored playedBy declarations could also be the pseudo identifier base, provided that Secondary is a nested team, which has a playedBy binding to Primary as its base class. This situation is part of the second example below (§2.7.(d)) (see T1 playedBy TB1).

(d) Implicit playedBy specialization

According to §2.1.(d) an implicit sub-role may implicitly specialize an existing playedBy relation. This requires the base class to be specified relative to some implicit (OuterTeam.this) or explicit (OuterTeam.base) team anchor. Specializing that team anchor automatically specializes the playedBy declaration, too. This rule never requires any action from a programmer but only explains the interpretation of a playedBy declaration in complex situations.

Two advanced examples demonstrating the above are:
  • If a role TOuter1.T.R of a nested team TOuter1.T is played by another role of the outer enclosing team TOuter1.B, subclassing the outer team TOuter1 to TOuter2 will produce a new role TOuter2.T.R which is automatically played by TOuter2.B, an implicit sub class of the original base class TOuter1.B.
Implicitly overriding playedBy
  • Consider the case where a nested T1 as a role of TOuter is stacked on a base team TB1. Also, T1 is a layered team over TB1 because its role R adapts role TB1.B.
    In this situation the playedBy relation of role TOuter.T1.R is given by a base-anchored type B<@T1.base>. If furthermore TOuter.T1 is subclassed to TOuter.T2 which covariantly refines the inherited playedBy declaration to TB2, then TOuter.T2.R will automatically refine the inherited playedBy relation to TB2.B to follow the new interpretation of the base anchor.
Implicitly overriding playedBy base