Context
A semantic scope moves values in two directions. Values flow in when a nested function reads a value declared in the scope around it, read down the call stack through ordinary closure capture. Values flow out when a nested function emits a value that an enclosing scope collects, gathered up the call stack. Both directions belong to the same scope, so both live on this page.
Values in: closures
A value declared in an outer Reactive.Function is captured by the functions defined inside it. The
inner function reads the value straight from the closure, however deep, with nothing passed by hand. This is the
way a value flows down a scope: it is in scope, so it is available.
const Select = Reactive.Function('Select', () => {
// state declared in the outer function
const selected = Reactive.State(Maths.Integer)(0);
// an inner function captures selected from the closure
const Option = Reactive.Function('Option', () => {
const index = Reactive.Argument(Maths.Integer);
// read selected here — no prop threaded through
Button.Set(selected, index, () => {
Content.Text("option");
});
});
Option(0);
Option(1);
});
The group owns selected; each option reads and writes it because it is in scope. Closures replaced
the earlier ambient mechanism, so a value shared down a scope is now the value already in scope.
Values out: emit and gather
Values also travel the other way. Reactive.Emit(channel, value) pushes a value onto a named
channel. A Reactive.Gather(channel) in an enclosing function collects everything emitted below it.
A channel behaves like a closure variable in reverse: where a closure flows a value down into nested scopes, a
gather flows values up out of them.
const List = Reactive.Function('List', () => {
// collect everything emitted on 'items' below here
const items = Reactive.Gather('items');
// items is the array of everything emitted below; build the output from it
});
const Row = Reactive.Function('Row', () => {
const label = Reactive.Argument(Text);
// push a value up onto the 'items' channel
Reactive.Emit('items', label);
});
Typed forms name the value on the channel. Reactive.Emit(Text)('items', value) emits a
value of a given type, and Reactive.Gather(Text)('items') collects that type. This is how
structural output is assembled: a list, a DOM tree, any container gathers what its children emit and builds the
result from it.
Why one page
Closures and gathers are the same scope read from both ends. A closure carries a value down into the functions a scope contains; a gather carries values up out of them. Naming a channel points a value at the scope that will collect it, the mirror of a name already in scope for the functions below to read. One scope, two directions.