Repository navigation
1.6 Captured Underlays
An Action's Underlay is selected when its Tag is applied.
For Ari:
Elf(ari)
Paladin(ari)Paladin captures the Attack visible after Elf.
If another Tag later adds yet another Attack, Paladin does not suddenly acquire a different Underlay.
Its relationship to Elf remains stable.
That lets us ask for different stages of Ari's composition:
assert Elf[ari].Attack() == \
"With elven grace, Attack!"
assert ari.Attack() == \
"With elven grace, Attack! For the oath!"ari.Attack() asks for the current Overlay.
Elf[ari].Attack() asks for the composition at Elf's layer.
Those are different coordinates in the same Agent.
There are two very different ways to reach another Action.
means:
use the contribution captured underneath this Action.
means:
resolve the Action visible on the Agent now.
For example:
class Combatant(Tag):
@Action
def Combat(agent):
return agent.Attack()Combat does not remember which Attack existed when Combatant was applied.
Every time Combat runs, it asks Ari's current Overlay for Attack.
So:
underlay() → captured contribution underneath me
agent.Attack() → contribution visible on the Agent now
This distinction is essential.
For an Action marked with @Underlay, TOP selects the contribution visible under the same name immediately before the new Action takes effect.
An Action named Attack asks for the preceding Attack.
The local parameter name does not affect selection:
@Action
@Underlay
def Attack(agent, previous):
return previous()and:
@Action
@Underlay
def Attack(agent, underlay):
return underlay()have the same Underlay semantics.
The second parameter receives the previously visible Action as a callable.
underlay()executes it.
The new Action decides what to do with its result:
return "before " + underlay() + " after"It may call its Underlay once, several times, conditionally, or not at all.
Cross-kind Underlays are treated separately in Pattern 5.
The captured contribution is whatever was visible on that Agent at application time.
It does not have to originate from a Base of the current Tag.
Base/Shape Geometry determines the Tag's Form.
The Agent's Overlay determines the contribution captured as Underlay.
Once an Action captures its Underlay, later layers do not alter that relationship.
A later Action may become visible above it, but the existing chain underneath remains the one that was captured.
When an Action has inputs, calling its Underlay without explicit arguments forwards the current Action call's arguments.
@Action
@Underlay
def Attack(agent, underlay, target, power=1):
return underlay() + f" with power {power}"Here, underlay() receives the current target and power.
Calling the Underlay with explicit arguments uses those arguments instead.
If @Underlay is requested but no previous Action exists under that name, TOP supplies a callable null Underlay.
underlay()does nothing and returns None.
The new Action decides whether that result is meaningful.
An Action declared without @Underlay does not automatically receive the previous Action.
It becomes the new visible contribution under that name.
Replacing an Action supplied by an unrelated Tag is allowed but diagnosed, because another semantic layer may have depended on the behaviour being replaced.