Skip to content

1.0.2 Layers and the Overlay

Julio Toboso edited this page Oct 26, 2026 · 2 revisions

Tag composition: Layers and the Overlay

Each Tag contributes a Layer. The Overlay is what the Agent presents.

An Agent can carry several Tags at once. Each contributes its own meaning, Records and Actions to the same object.

In the previous example, Elf gave Ari a Languages Record and an Attack Action. What happens when another Tag also provides an Action called Attack?

TOP resolves contributions independently. The most recently applied contribution with that name becomes visible. Other contributions remain available.

Adding another way to attack

Continuing with Ari from the previous page:

class Fighter(Tag):

    # Another Attack Action
    @Action
    def Attack(agent):
        return f"{agent.name} cuts with a blade."


Fighter(ari)
# This will raise a warning informing us of the conflict of names.
# It will solve towards the latest name declared. 

assert ari.Attack() == "Ari cuts with a blade."
assert ari.Languages == "Elven, Sylvan, Common"

Fighter contributes a new Attack, so ari.Attack() now selects that version. It contributes no Languages Record, so Ari keeps the one provided by Elf.

Ari still carries both Tags:

assert ari in Elf
assert ari in Fighter
---
title: Ari
---
swimlane-beta TD
    subgraph Fighter
        fighter_attack("Attack")
    end

    subgraph Elf
        elf_attack("Attack")
        languages["Languages"] --- lng[\"Elven, <br> Sylvan, <br>Common"\]
    end

    subgraph Character
        name["name: Ari"]
        level["level: 3"]
    end

    fighter_attack <===x elf_attack
    fighter_attack <-.-o name
    elf_attack <-.-o name
Loading

Calling ari.Attack() runs Fighter’s Action now. The thick connection represents overlaying; it does not mean that Fighter automatically calls Elf’s Action as well. The x next to elf's attack shows it was overwritten.

Note: TopKit warns when an Action replaces one from an independent Tag without an Underlay. The replacement is allowed. Extending the previous Action through an Underlay is a separate composition choice.

The Overlay is the current combination

The Overlay may draw different names from different layers. Here:

  • ari.Attack resolves to the Action provided by Fighter.
  • ari.Languages holds the Record contributed by Elf.
  • ari.name and ari.level remain attributes supplied by Character.

Applying a Tag therefore does not replace the Agent’s entire interface. It contributes to the composition, changing the names it provides.

A Tag view selects an earlier composition

Sometimes we want to use the composition established by an earlier Tag.

Elf[ari] is an Agent-bound Tag view: it selects the Overlay established immediately after Elf’s current application to Ari. Adding Fighter does not redirect that view’s Attack.

# Ari's current Overlay:
assert ari.Attack() == "Ari cuts with a blade."

# The composition established after Elf:
assert Elf[ari].Attack() == "Ari attacks with elven grace."

Both calls concern the same Ari. The access path determines which composition supplies the Action.

A view includes the earlier contributions available when that Tag was applied, including contributions from the host Class. It is more than a collection of methods declared inside the Tag.

The view is read-only and does not create a copy of Ari. Its guarantee concerns which layers it selects; it does not freeze all underlying data.

Asking whether a contribution exists

To check whether the current overlay provides a named contribution, you can check for the name using @:

assert "Attack" @ ari
assert "Languages" @ ari

These expressions ask whether the names are present. They do not call Attack or test the value held in Languages.

Clone this wiki locally