Expand description
The signal graph, as a graph.
docs/03-type-and-effect-system.md §3.7:
“The signal graph is a graph, not a pipeline. This section reads top-to-bottom, and the
programs it describes do not: events is decided from the state, and the state is folded from
events. The cycle is real and it is sound.”
Phase 1 and Phase 2 read the graph by recognising one shape: find the merge_clients(), find
the durable, find the decide, find the first client-placed signal, and inline everything
between them (docs/19-phase-1-report.md §19.9). That
was legitimate narrowness because it announced itself — nine diagnostics refused every other
shape — and it was named as debt by two phases running. It also had a hole neither report knew
about: a program with two durable folds matched the shape, was accepted, and was sliced with
both folds reading the same accumulator. See
docs/23-general-slicer-report.md §23.2.
This module is the replacement. It does not recognise a shape. It builds the graph the program
wrote — one vertex per signal operation, including the ones nested inside a declaration —
computes its strongly connected components, and hands crate::split a structure to slice.
What used to be “the durable one” is now “the vertices whose op is Op::Durable”, and there
may be any number of them.
§What a vertex is
A declared signal contributes one vertex per prim application in its expression, not one per
declaration. todos: Signal[State] = durable(fold(apply_event, empty, events)) is two vertices
— a Op::Durable over a Op::Fold — because the fold is a node in the dataflow whether or
not the program gave it a name. Only the outermost carries the declared name; the inner one is
labelled todos·fold so a diagnostic and beck explain flow can still point at it.
That is the difference between a graph and a pattern: map2(f, durable(fold(…)), summary)
needs no new case here, because there was never a case to begin with.
§Cycles
The condensation is computed by crate::graph::DepGraph, which already does Tarjan
iteratively over a CSR adjacency and numbers components in topological order. Reusing it rather
than writing a second SCC pass is the point of it being a separate module.
One rule is imposed on the result: every cycle must contain a fold. The decide → durable → fold → decide cycle is sound because the fold is where the recursion bottoms out — the
accumulator is a value the slicer can take as a parameter. A cycle of pure signal_maps has no
such point and is a program with no meaning; Graph::build refuses it by name rather than
looping.
Structs§
Enums§
- Op
- What a vertex does. One variant per construct in §3.7’s signal vocabulary.
Constants§
- FUSED_
STATE - The accumulator a program with several durable folds is compiled to.
Functions§
- durables
- Every
durablea set of signal declarations holds, labelled exactly asGraph::buildlabels it, in declaration order. - fused_
state_ decl - The synthetic accumulator a program with several durable folds is compiled to.
- signal_
elem - The element a
Signal[T]orStream[T]carries.
Type Aliases§
- SigId
- An index into
Graph::nodes.