Repository F# setup
open System
open System.IO
open System.Threading
open System.Threading.Tasks
open Axial
open Axial.Layers
open Axial.Console
open Axial.FileSystem
open Axial.Hosting
open Axial.Hosting.Browser
open Axial.Hosting.Node
open Axial.HttpClient
open Axial.PlatformService
open Axial.Process
open Axial.State
open Axial.Telemetry
open Axial.Telemetry.JavaScriptFibers
Fibers represent running child workflows.
In Axial, a Fiber is a handle to a running Flow. A flow is a cold description of work. A fiber is the hot execution that exists after that work has been started in the background.
The Mental Model
While a Flow is cold (a description of work that hasn't started yet), a Fiber is hot (the work is currently being executed).
When you fork a flow, you are saying: start this work now, give me a typed handle to it, and let the current workflow continue. That handle is the fiber.
let loadBoth left right =
flow {
let! leftFiber = Flow.fork left
let! rightValue = right
let! leftValue = Flow.join leftFiber
return leftValue, rightValue
}
loadBoth: Flow<'a,'b,'c> -> Flow<'a,'b,'d> -> Flow<'a,'b,('c * 'd)>left: Flow<'a,'b,'c>right: Flow<'a,'b,'d>flow: FlowBuilderThe universal flow { } computation expression.
leftFiber: Fiber<'b,'c>Axial.Flowfork: Flow<'env,'error,'value> -> Flow<'env,'none,Fiber<'error,'value>>Starts a flow in a new fiber without waiting for it to complete. Forking turns a cold flow description into hot child work and returns a handle that can later be joined or interrupted. Prefer zipPar or race when the caller only needs a simple parallel composition. The flow to fork. A flow that produces a handle.
rightValue: 'dleftValue: 'cjoin: Fiber<'error,'value> -> Flow<'env,'error,'value>Waits for a fiber to complete and returns its successful value or typed failure. Joining preserves the child workflow's error channel. If the child failed with Cause.Fail, the joined flow fails with the same typed error; interruption and defects remain interruption and defects. The fiber to join. A flow that completes with the fiber's outcome.
Structured Concurrency
Fibers are the foundation of Structured Concurrency in Axial. Unlike "fire-and-forget" background tasks, Fibers allow you to maintain a parent-child relationship between workflows, ensuring that background work is always accounted for and safely cleaned up.
The primary operations for managing fibers are:
Flow.fork: starts a flow in the background and returns aFiber<'error, 'value>handle.Flow.join: waits for the fiber and resumes with its successful value or typed failure.Flow.interrupt: asks the fiber to stop, then waits for the child workflow to report its finalExit.Flow.forkDetached: starts deliberate fire-and-forget work whose defects are never reported as unobserved.Flow.forkNamed: forks with a diagnostic name that carries into dumps and telemetry fiber spans, so long-lived background fibers are recognizable instead of bare ids.Fiber.dump: returns a diagnostic snapshot of one fiber handle.
Joining or interrupting a fiber marks its outcome as observed. A fiber whose handle is simply discarded and that later dies with a defect is reported through the runtime's fiber observer; use Flow.forkDetached when the silence is intentional, and Flow.Runtime.supervise to restart background work that dies with defects.
Why Fibers?
Fibers provide several advantages over raw Task or Async values:
Interruption
In ordinary .NET code, cancellation often depends on manually threading a CancellationToken through every layer. In Axial, interruption is part of the execution model. Flow.interrupt signals the child fiber and waits for it to finish, so callers can observe the final Exit<'value, 'error>.
Typed Outcomes
A Fiber<'error, 'value> remembers the error type and success type of the workflow it is running. When you Flow.join a fiber, the joined flow has the same typed failure channel as the child.
Clear Ownership
Fibers make background work visible in the workflow that started it. If the parent needs the result, it joins. If the parent no longer needs the result, it interrupts. That is different from launching an untracked task and hoping some other layer notices when it fails.
Diagnostics
Every forked fiber carries metadata:
FiberId: A unique runtime id for the child fiber.Name: The diagnostic name fromFlow.forkNamed, if one was given.ParentId: The id of the fiber that calledFlow.fork.Annotations: The runtime annotations (Flow.annotate) in scope at the fork site.StartedAt/SettledAt: UTC timestamps for fork and settle.Status:Running,Succeeded,Failed, orInterrupted.
Use Fiber.dump when logging or debugging one fiber. The dump is a snapshot, so a running fiber can report Running before Flow.join and Succeeded, Failed, or Interrupted afterward. To see every live fiber at once as a parent/child tree, install a FiberRegistry with Flow.withFiberRegistry and call registry.Dump() — see Observability.
Underlying Implementation
On .NET, a fiber is a small record around a Task<Exit<'value, 'error>>, a CancellationTokenSource, and diagnostic metadata. On Fable, it wraps an Async<Exit<'value, 'error>> with the same public model.
type Fiber<'error, 'value> =
{
ExitTask: Task<Exit<'value, 'error>> // The running work
InterruptSource: CancellationTokenSource // The kill switch
Metadata: FiberMetadata // Diagnostic identity and lifecycle state
}
08-concurrency-and-state_01-fibers.md_page.Fiber`2errorvalueExitTask: Task<Exit<'value,'error>>System.Threading.Tasks.Task`1Represents an asynchronous operation that can return a value. The type of the result produced by this .
Axial.Exit`2Represents the final outcome of a workflow execution. The type of the success value. The type of the domain-specific failure value.
InterruptSource: CancellationTokenSourceSystem.Threading.CancellationTokenSourceSignals to a that it should be canceled.
Metadata: FiberMetadataAxial.FiberMetadataDiagnostic metadata for a running fiber.
Concurrency Primitives
Most code should not manage fibers manually. Prefer high-level parallel combinators when they express the whole relationship:
Flow.zipPar: Runs two flows concurrently in separate fibers and waits for both.Flow.race: Runs two flows concurrently and returns the result of the winner, interrupting the loser.
Use explicit fibers when the parent workflow needs to start child work, do something else, and decide later whether to join or interrupt it.

