topo
creates a hierarchy of scoped, nested [environments][topo::Env] whose shape matches the
function callgraph. These environments store singletons indexed by their type, and references to
environmental values are available only to an enclosed call scope. When a #![topo::nested]
function is called, its parent environment is cheaply propagated along with any additional
values added at appropriate callsites.
Each environment in this hierarchy has a unique and deterministic [topo::Id
] describing that
environment and the path taken to arrive at its stack frame. These identifiers are derived from
the path taken through the callgraph to the current location, and are stable across repeated
invocations of the same execution paths.
By running the same topologically-nested functions in a loop, we can observe changes to the structure over time. The moxie crate uses these identifiers and environments to create persistent trees for rendering human interfaces.
Making functions nested within the call topology
Defining a topological function results in a macro definition for binding the
function to each callsite where it is invoked. Define a topologically-nested function with the
topo::nested
attribute:
#[topo::nested]
fn basic_topo() -> topo::Id { topo::Id::current() }
#[topo::nested]
fn tier_two() -> topo::Id { basic_topo!() }
// each of these functions will be run in separately identified
// contexts as the source locations for their calls are different
let first = basic_topo!();
let second = basic_topo!();
assert_ne!(first, second);
let third = tier_two!();
let fourth = tier_two!();
assert_ne!(third, fourth);
assert_ne!(first, third);
assert_ne!(first, fourth);
assert_ne!(second, fourth);
Because topological functions must be sensitive to the location at which they're invoked and within their immediate parent, we transform the function definition into a macro to track the source location at which it is called. Future language features may make it possible to call topo-nested functions without any special syntax.