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.PlatformService
open Axial.State
open Axial.Telemetry
open Axial.Telemetry.JavaScript

Getting Started

A stream pipeline has three parts:

  1. A source describes where values come from.
  2. Operators transform values one pull at a time.
  3. A terminal consumer turns the stream back into a Flow that can run.
> (FlowStream.fromSeq [ 1..10 ] : FlowStream<int>)
- |> FlowStream.filter (fun value -> value % 2 = 0)
- |> FlowStream.map (fun value -> value * 10)
- |> FlowStream.runFold (+) 0
- |> Flow.run ();;
val it: Exit<int,Never> = Success 300

Constructing the pipeline does not enumerate the sequence. Flow.run starts the terminal Flow. FlowStream.runFold then pulls one value at a time through filter and map, retaining only the running total.

Keep effects in Flow

Use FlowStream.mapFlow when transforming one value requires an effect:

> (FlowStream.fromSeq [ "a"; "b"; "c" ] : FlowStream<string>)
- |> FlowStream.mapFlow (fun letter -> Flow.succeed (letter.ToUpperInvariant()))
- |> FlowStream.runCollect
- |> Flow.run ();;
val it: Exit<string list,Never> = Success ["A"; "B"; "C"]

A mapping failure stops further pulls and remains in the stream's typed error channel. Cancellation reaches the active operation through the enclosing Flow runtime.

Choose the terminal operation deliberately

FlowStream.runCollect is convenient for a known finite stream, but it stores every value. Prefer FlowStream.runFold, FlowStream.runForEach, or FlowStream.runForEachFlow when values can be handled incrementally.

Terminal consumers create a child scope. Completion, failure, interruption, and early termination close stream-owned resources and child fibers before the terminal Flow returns.

Next, read Constructing streams.