nustack

Span

Transparent Interaction. The Interaction sub-kind that wraps a body to define a region (lifecycle, policy) governing how it runs. Does no work of its own; it forwards the body's yield in the same shape and protocol. The Span atom is always a Span - parents slot-fit a Span by looking through to the body's yield, not by re-typing.

Properties

  • Own effects: any. no restriction.
  • Children: body: any Nu (Ref, Query, Command, Flow, or another Span), auxiliary children: any.
  • Yields: exactly what the body yields, in the same shape (one value, a stream, or nothing).

Sub-shapes

Sub-shapeGovernsHooks / mechanismExamples
Bracketlifecyclebefore / after / after_failureSnapshot, Transaction
Policyexecutionre-run / fall back on failureRetry, TryCatch

Forms

# Bracket
Snapshot(body)
Transaction(body)

# Policy
Retry(body, attempts_q)
TryCatch(body, fallback_body)

Examples

ExpressionBody kindSpan sub-shapeBody yields
Snapshot(view_ref)RefBracketone value
Snapshot(ForEach(items_q, x+1))QueryBracketa stream
Snapshot(ForEachDo(items, Print))FlowBracketnothing
Transaction(ForEachDo(items, Store(ref[i], v)))FlowBracketnothing
Retry(Store(ref, q), 3)CommandPolicynothing
TryCatch(Transaction(body), fallback)SpanPolicybody-dependent

Composition

  • One body child. Required. The Span is meaningless without it.
  • Body can be any Nu: Ref, Query, Command, Flow, or another Span. Span is cross-cutting.
  • Spans nest. Transaction(Snapshot(body)) is fine - each adds its own region around the inner body.
  • Auxiliary children (e.g. attempts_q in Retry, fallback_body in TryCatch) sit alongside the main body; the Span consumes them internally.

Constraints

  • Body is required. Empty Span is structurally invalid - nothing to wrap.
  • Yield-shape mirrors body. A Span over a stream body yields a stream; over a scalar body yields a scalar; over a body that yields nothing, yields nothing. The Span atom remains a Span; only the yield is forwarded.

Atomic re-evaluation

A Span that re-runs or rolls back its body (Retry, Transaction) treats each attempt as a fresh, complete evaluation of the body, whatever its cardinality - the body is one child, and re-running it does not depend on its shape. For a stream body this means the attempt realizes the stream inside the region (drains it, emits only on success); a retry never resumes a half-drained stream or replays an already-emitted prefix. Bounded streams only - an unbounded body never completes an attempt.

Why this sub-kind

Some concerns - lifecycle (snapshot, transaction), execution policy (retry, fallback) - apply uniformly to Queries, Commands, and Flows alike. Folding them into one of the work sub-kinds would either pollute that sub-kind with control concerns or duplicate the construct three times (one per work sub-kind).

Span isolates these concerns as a fourth, transparent sub-kind of Interaction. The body does what it does; the Span shapes the surroundings. Composition continues to see the body's yield - the Span atom forwards it unchanged.

On this page