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.JavaScriptConstructing Streams
An array or seq is the right source when all values already exist and producing the next value is synchronous. Wrapping
one in a FlowStream becomes useful when it must compose with effectful sources and incremental consumers under one
failure, cancellation, backpressure, and cleanup model.
Use FlowStream.fromSeq, FlowStream.singleton, and FlowStream.empty for those existing values:
Shared setup
// Setup for the checked examples on this page.
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
/// Fails the docs test when an example's result differs from the value shown.
let shouldEqual expected actual =
if actual <> expected then failwithf "Expected %A but got %A" expected actual
SystemIOThreadingTasksAxialLayersConsoleFileSystemHostingBrowserNodePlatformServiceStateTelemetryJavaScriptshouldEqual: 'a -> 'a -> unitexpected: 'aactual: 'a(<>): 'T -> 'T -> boolStructural inequality The first parameter. The second parameter. The result of the comparison. 5 <> 5 // Evaluates to false 5 <> 6 // Evaluates to true [1; 2] <> [1; 2] // Evaluates to false
failwithf: Printf.StringFormat<'T,'Result> -> 'TPrint to a string buffer and raise an exception with the given result. Helper printers must return strings. The formatter. The formatted result. See Printf.failwithf (link: ) for examples.
> (FlowStream.fromSeq [ 1..100 ] : FlowStream<int>)
- |> FlowStream.take 3
- |> FlowStream.append (FlowStream.singleton 42)
- |> FlowStream.runCollect
- |> Flow.run ();;val it: Exit<int list,Never> = Success [1; 2; 3; 42]FlowStream.empty emits nothing. FlowStream.fromSeq obtains its enumerator only when consumption starts. The enumerator is registered with the stream's Flow
scope and disposed after completion or early termination.
Lift one effect
FlowStream.fromFlow creates a stream containing the successful result of one Flow:
> (Flow.succeed "Ada" : Flow<string>)
- |> FlowStream.fromFlow
- |> FlowStream.runCollect
- |> Flow.run ();;val it: Exit<string list,Never> = Success ["Ada"]A failed Flow fails the stream before producing a value.
Unfold effectful state
FlowStream.unfoldFlow repeatedly runs an effectful state transition. Return Some(value, nextState) to emit a value
and remember the state for the next pull, or return None to finish.
The value and state are separate because they serve different audiences. The value goes downstream; the state stays inside the source and tells it how to continue. A paginated source might emit a page of messages while retaining an opaque continuation token. A socket parser might emit a decoded frame while retaining unread bytes. Requiring only a next state would either emit implementation state to consumers or restrict unfolding to sources whose state happens to be their output.
> (1
- |> FlowStream.unfoldFlow (fun number ->
- Flow.succeed (
- if number > 3 then None
- else Some(number, number + 1)))
- : FlowStream<int>)
- |> FlowStream.runCollect
- |> Flow.run ();;val it: Exit<int list,Never> = Success [1; 2; 3]Only one step runs per downstream pull. In this small numeric example the emitted value and next state look similar, but they are independent parts of the transition.
Fetch a known list concurrently
Suppose fetchHtml performs one HTTP request as a Flow, while extractLinks is a total function from an HTML document
to the links it contains. When the URLs are already known, construct a stream from the list and overlap requests with
FlowStream.mapFlowPar:
The examples stand in for HTTP with a small in-memory site, where each page lists the pages it links to:
type HtmlPage = { Url: string; Html: string }
let site =
Map [ "/", "links: /docs /blog"
"/docs", "links: /docs/flow /"
"/blog", "links: /"
"/docs/flow", "links:" ]
let fetchHtml (url: string) : Flow<ClockEnvironment, Never, string> =
Flow.sleep (TimeSpan.FromMilliseconds 1.0) |> Flow.map (fun () -> Map.find url site)
let extractLinks (html: string) : string list =
html.Split(' ', StringSplitOptions.RemoveEmptyEntries) |> Array.skip 1 |> List.ofArray
FsLiveDocsGeneratedPage2_7555C511A9CE.HtmlPageUrl: stringstringAn abbreviation for the CLI type . Basic Types
Html: stringsite: Map<string,string>``.ctor``: (string * string) seq -> Map<string,string>Builds a map that contains the bindings of the given IEnumerable. The input sequence of key/value pairs. The resulting map. This is an O(n log n) operation, where n is the number of elements in the sequence. Map [ (1, "a"); (2, "b") ] // evaluates to map [(1, "a"); (2, "b")]
fetchHtml: string -> Flow<ClockEnvironment,Never,string>url: stringAxial.Flow`3Represents a cold workflow that reads an environment, returns a typed result, and is executed explicitly through one of its execution members such as ToTask, ToAsync, or RunSynchronously. The type of the environment dependency. The type of the failure value. The type of the success value.
Axial.ClockEnvironmentAn environment containing only a clock, for timed flows with no other services.
Axial.NeverRepresents an error channel that cannot occur.
Axial.Flowsleep: TimeSpan -> Flow<'env,'error,unit>Suspends the flow for the specified duration, observing cancellation. The duration to sleep. A flow that completes after the specified delay, or is interrupted if cancelled first.
System.TimeSpanRepresents a time interval.
FromMilliseconds: float -> TimeSpanReturns a that represents a specified number of milliseconds. A number of milliseconds. An object that represents . is less than or greater than . -or- is . -or- is . is equal to .
(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
map: ('value -> 'next) -> Flow<'env,'error,'value> -> Flow<'env,'error,'next>Transforms the successful value of a flow. If the source fails, the is not executed. The original failure cause is preserved, including typed failures, interruption, and defects. Use map for pure value transformations after an effect has succeeded. A function of type 'value -> 'next to transform the successful value. The source flow of type to transform. A new with the transformed success value of type 'next. let flow = Flow.succeed 1 |> Flow.map (fun x -> x + 1)
Microsoft.FSharp.Collections.MapModuleContains operations for working with values of type .
find: 'Key -> Map<'Key,'T> -> 'TLookup an element in the map, raising KeyNotFoundException if no binding exists in the map. The input key. The input map. Thrown when the key does not exist in the map. The value mapped to the given key. Maps are represented as binary trees so this is an O(log n) operation, where n is the number of bindings in the map. let sample = Map [ (1, "a"); (2, "b") ] sample |> Map.find 1 // evaluates to "a" sample |> Map.find 3 // throws KeyNotFoundException
extractLinks: string -> string listhtml: stringlistThe type of immutable singly-linked lists. See the module for further operations related to lists. Use the constructors [] and :: (infix) to create values of this type, or the notation [1; 2; 3]. Use the values in the List module to manipulate values of this type, or pattern match against the values directly. See also F# Language Guide - Lists.
Split: char * StringSplitOptions -> string arraySystem.StringSplitOptionsSpecifies whether applicable method overloads include or omit empty substrings from the return value.
RemoveEmptyEntries: StringSplitOptionsThe return value does not include array elements that contain an empty string
Microsoft.FSharp.Collections.ArrayModuleContains operations for working with arrays. See also F# Language Guide - Arrays.
skip: int -> 'T array -> 'T arrayBuilds a new array that contains the elements of the given array, excluding the first N elements. The number of elements to skip. If negative the full array will be returned as a copy. The input array. A copy of the input array, after removing the first N elements. Thrown when the input array is null. Thrown when count exceeds the number of elements in the array. let inputs = [| "a"; "b"; "c"; "d" |] inputs |> Array.skip 2 Evaluates to [| "c"; "d" |] let inputs = [| "a"; "b"; "c"; "d" |] inputs |> Array.skip 5 Throws ArgumentException. let inputs = [| "a"; "b"; "c"; "d" |] inputs |> Array.skip -1 Evaluates to [| "a"; "b"; "c"; "d" |]. This is an O(n) operation, where n is the length of the array.
Microsoft.FSharp.Collections.ListModuleContains operations for working with values of type . Operations for collections such as lists, arrays, sets, maps and sequences. See also F# Collection Types in the F# Language Guide.
ofArray: 'T array -> 'T listBuilds a list from the given array. The input array. The list of elements from the array. let inputs = [| 1; 2; 5 |] inputs |> List.ofArray Evaluates to [ 1; 2; 5 ]. This is an O(n) operation, where n is the length of the array.
let fetchKnownPages (urls: string list) : FlowStream<ClockEnvironment, Never, HtmlPage> =
urls
|> FlowStream.fromSeq
|> FlowStream.mapFlowPar
(Parallelism.bounded 4)
(fun url ->
fetchHtml url
|> Flow.map (fun html ->
{ Url = url
Html = html }))
fetchKnownPages: string list -> FlowStream<ClockEnvironment,Never,HtmlPage>urls: string liststringAn abbreviation for the CLI type . Basic Types
listThe type of immutable singly-linked lists. See the module for further operations related to lists. Use the constructors [] and :: (infix) to create values of this type, or the notation [1; 2; 3]. Use the values in the List module to manipulate values of this type, or pattern match against the values directly. See also F# Language Guide - Lists.
Axial.FlowStream`3Represents a cold stream of values that requires an environment, can fail with a typed error, and supports backpressure. The type of the environment dependency. The type of the failure value. The type of the success values in the stream.
Axial.ClockEnvironmentAn environment containing only a clock, for timed flows with no other services.
Axial.NeverRepresents an error channel that cannot occur.
FsLiveDocsGeneratedPage2_7555C511A9CE.HtmlPage(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
Axial.FlowStreamModulefromSeq: 'value seq -> FlowStream<'env,'error,'value>Creates a stream from a synchronous sequence of values. The sequence of values to be emitted by the stream. A that yields each value from the sequence. FlowStream.fromSeq [1..10] |> FlowStream.runCollect |> Flow.run ()
mapFlowPar: Parallelism -> ('a -> Flow<'b,'c,'d>) -> FlowStream<'b,'c,'a> -> FlowStream<'b,'c,'d>Maps values with a continuously replenished, bounded set of child fibers. Results are emitted in completion order. After each result is consumed, the next upstream value starts, so there are no strict batch barriers. At most the configured number of mappings are active or retained. The first failure observed stops the stream; the terminal consumer's child scope interrupts and awaits all remaining mappings before returning.
Axial.ParallelismModuleCreates bounds for parallel Flow and stream operators.
bounded: int -> ParallelismCreates a positive concurrency bound. Thrown when is not positive.
url: stringfetchHtml: string -> Flow<ClockEnvironment,Never,string>Axial.Flowmap: ('value -> 'next) -> Flow<'env,'error,'value> -> Flow<'env,'error,'next>Transforms the successful value of a flow. If the source fails, the is not executed. The original failure cause is preserved, including typed failures, interruption, and defects. Use map for pure value transformations after an effect has succeeded. A function of type 'value -> 'next to transform the successful value. The source flow of type to transform. A new with the transformed success value of type 'next. let flow = Flow.succeed 1 |> Flow.map (fun x -> x + 1)
html: stringUrl: stringHtml: stringmapFlowPar emits pages in completion order, so sort before comparing:
fetchKnownPages [ "/"; "/docs"; "/blog" ]
|> FlowStream.map _.Url
|> FlowStream.runCollect
|> Flow.map List.sort
|> Flow.run (ClockEnvironment Clock.live)
|> shouldEqual (Exit.Success [ "/"; "/blog"; "/docs" ])
fetchKnownPages: string list -> FlowStream<ClockEnvironment,Never,HtmlPage>(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
Axial.FlowStreamModulemap: ('v -> 'w) -> FlowStream<'env,'error,'v> -> FlowStream<'env,'error,'w>Transforms the successful values of a stream using the provided function. The function to transform each value. The stream whose values should be transformed. A new stream that yields transformed values. let stream = FlowStream.fromSeq [1; 2; 3] |> FlowStream.map (fun n -> n * 2)
_arg1: HtmlPageUrl: stringrunCollect: FlowStream<'a,'b,'c> -> Flow<'a,'b,'c list>Collects all emitted values into a list. stream |> FlowStream.runCollect
Axial.Flowmap: ('value -> 'next) -> Flow<'env,'error,'value> -> Flow<'env,'error,'next>Transforms the successful value of a flow. If the source fails, the is not executed. The original failure cause is preserved, including typed failures, interruption, and defects. Use map for pure value transformations after an effect has succeeded. A function of type 'value -> 'next to transform the successful value. The source flow of type to transform. A new with the transformed success value of type 'next. let flow = Flow.succeed 1 |> Flow.map (fun x -> x + 1)
Microsoft.FSharp.Collections.ListModuleContains operations for working with values of type . Operations for collections such as lists, arrays, sets, maps and sequences. See also F# Collection Types in the F# Language Guide.
sort: 'T list -> 'T listSorts the given list using . This is a stable sort, i.e. the original order of equal elements is preserved. This is an O(n log n) operation, where n is the length of the list. The input list. The sorted list. let input = [8; 4; 3; 1; 6; 1] List.sort input Evaluates to [1; 1; 3; 4; 6; 8].
run: 'env -> Flow<'env,'error,'value> -> Exit<'value,'error>Runs the workflow and blocks until the final exit is available. The environment used by the workflow. The workflow to run. The final workflow exit. let exit = workflow |> Flow.run environment
``.ctor``: IClock -> ClockEnvironmentAxial.PlatformService.ClockHelpers for the clock service.
live: IClockCreates a live clock backed by and a monotonic timer.
shouldEqual: 'a -> 'a -> unitAxial.Exit`2Represents the final outcome of a workflow execution. The type of the success value. The type of the domain-specific failure value.
SuccessThe workflow completed successfully.
This keeps at most four requests active or waiting to be emitted. Pages arrive in completion order. Stopping downstream also interrupts and awaits requests that are still running.
Discover and pull linked pages
A crawler does not know every URL up front. Each response discovers more work. Here the unfold state is the private crawl frontier (pending URLs plus the URLs already seen), while each emitted value is a fetched page:
type CrawlState =
{ Pending: string list
Seen: Set<string> }
let fetchPagesAndDiscoverLinks (seen: Set<string>) (urls: string list) : Flow<ClockEnvironment, Never, HtmlPage list * string list> =
urls
|> List.map (fun url ->
fetchHtml url
|> Flow.map (fun html ->
{ Url = url
Html = html }))
|> Flow.sequencePar
|> Flow.map (fun pages ->
let discovered =
pages
|> List.collect (fun page -> extractLinks page.Html)
|> List.filter (fun url -> not (Set.contains url seen))
pages, discovered)
let crawl (seeds: string list) : FlowStream<ClockEnvironment, Never, HtmlPage> =
{ Pending = seeds
Seen = Set.empty }
|> FlowStream.unfoldFlow (fun state ->
let batch =
state.Pending
|> List.filter (fun url -> not (Set.contains url state.Seen))
|> List.distinct
|> List.truncate 4
if List.isEmpty batch then
Flow.succeed None
else
let seen = Set.union state.Seen (Set.ofList batch)
let remaining = List.except batch state.Pending
flow {
let! pages, discovered =
fetchPagesAndDiscoverLinks seen batch
return
Some(
pages,
{ Pending = remaining @ discovered
Seen = seen })
})
|> FlowStream.collect FlowStream.fromSeq
FsLiveDocsGeneratedPage2_7555C511A9CE.CrawlStatePending: string liststringAn abbreviation for the CLI type . Basic Types
listThe type of immutable singly-linked lists. See the module for further operations related to lists. Use the constructors [] and :: (infix) to create values of this type, or the notation [1; 2; 3]. Use the values in the List module to manipulate values of this type, or pattern match against the values directly. See also F# Language Guide - Lists.
Seen: Set<string>Microsoft.FSharp.Collections.FSharpSet`1Immutable sets based on binary trees, where elements are ordered by F# generic comparison. By default comparison is the F# structural comparison function or uses implementations of the IComparable interface on element values. See the module for further operations on sets. All members of this class are thread-safe and may be used concurrently from multiple threads.
fetchPagesAndDiscoverLinks: Set<string> -> string list -> Flow<ClockEnvironment,Never,(HtmlPage list * string list)>seen: Set<string>urls: string listAxial.Flow`3Represents a cold workflow that reads an environment, returns a typed result, and is executed explicitly through one of its execution members such as ToTask, ToAsync, or RunSynchronously. The type of the environment dependency. The type of the failure value. The type of the success value.
Axial.ClockEnvironmentAn environment containing only a clock, for timed flows with no other services.
Axial.NeverRepresents an error channel that cannot occur.
FsLiveDocsGeneratedPage2_7555C511A9CE.HtmlPage(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
Microsoft.FSharp.Collections.ListModuleContains operations for working with values of type . Operations for collections such as lists, arrays, sets, maps and sequences. See also F# Collection Types in the F# Language Guide.
map: ('T -> 'U) -> 'T list -> 'U listBuilds a new collection whose elements are the results of applying the given function to each of the elements of the collection. The function to transform elements from the input list. The input list. The list of transformed elements. let inputs = [ "a"; "bbb"; "cc" ] inputs |> List.map (fun x -> x.Length) Evaluates to [ 1; 3; 2 ] This is an O(n) operation, where n is the length of the list.
url: stringfetchHtml: string -> Flow<ClockEnvironment,Never,string>Axial.Flowmap: ('value -> 'next) -> Flow<'env,'error,'value> -> Flow<'env,'error,'next>Transforms the successful value of a flow. If the source fails, the is not executed. The original failure cause is preserved, including typed failures, interruption, and defects. Use map for pure value transformations after an effect has succeeded. A function of type 'value -> 'next to transform the successful value. The source flow of type to transform. A new with the transformed success value of type 'next. let flow = Flow.succeed 1 |> Flow.map (fun x -> x + 1)
html: stringUrl: stringHtml: stringsequencePar: Flow<'env,'error,'value> list -> Flow<'env,'error,'value list>Runs all flows concurrently and returns their values in input order. An empty input succeeds immediately. If any flow fails, remaining flows are interrupted through the same structured parallel composition as zipPar.
pages: HtmlPage listdiscovered: string listcollect: ('T -> 'U list) -> 'T list -> 'U listFor each element of the list, applies the given function. Concatenates all the results and returns the combined list. The function to transform each input element into a sublist to be concatenated. The input list. The concatenation of the transformed sublists. For each positive number in the array we are generating all the previous positive numbers [1..4] |> List.collect (fun x -> [1..x]) The sample evaluates to [1; 1; 2; 1; 2; 3; 1; 2; 3; 4] (added extra spaces for easy reading) This is an O(n) operation, where n is the total length of all resulting sublists.
page: HtmlPageextractLinks: string -> string listfilter: ('T -> bool) -> 'T list -> 'T listReturns a new collection containing only the elements of the collection for which the given predicate returns "true" The function to test the input elements. The input list. A list containing only the elements that satisfy the predicate. let input = [1, "Luke"; 2, "Kirk"; 3, "Kenobi"; 4, "Spock"] let isEven x = 0 = x % 2 let isComingFromStarTrek (x,_) = isEven x input |> List.filter isComingFromStarTrek Evaluates to [(2, "Kirk"); (4, "Spock")] This is an O(n) operation, where n is the length of the list.
``not``: bool -> boolNegate a logical value. Not True equals False and not False equals True The value to negate. The result of the negation. not (2 + 2 = 5) // Evaluates to true // not is a function that can be compose with other functions let fileDoesNotExist = System.IO.File.Exists >> not
Microsoft.FSharp.Collections.SetModuleContains operations for working with values of type .
contains: 'T -> Set<'T> -> boolEvaluates to "true" if the given element is in the given set. The element to test. The input set. True if element is in set. let set = Set.empty.Add(2).Add(3) printfn $"Does the set contain 1? {set.Contains(1))}" The sample evaluates to the following output: Does the set contain 1? false This is an O(log n) operation, where n is the number of elements in the set.
crawl: string list -> FlowStream<ClockEnvironment,Never,HtmlPage>seeds: string listAxial.FlowStream`3Represents a cold stream of values that requires an environment, can fail with a typed error, and supports backpressure. The type of the environment dependency. The type of the failure value. The type of the success values in the stream.
empty: Set<'T>The empty set for the type 'T. Set.empty<int> Evaluates to set [ ]. This is an O(1) operation.
Axial.FlowStreamModuleunfoldFlow: ('state -> Flow<'env,'error,('value * 'state) option>) -> 'state -> FlowStream<'env,'error,'value>Creates a cold stream by repeatedly running an effectful state transition. Returns Some(value, nextState) or None when the stream is complete. The state used for the first pull. FlowStream.unfoldFlow (fun n -> Flow.ok (if n < 3 then Some(n, n + 1) else None)) 0
state: CrawlStatebatch: string listdistinct: 'T list -> 'T listReturns a list that contains no duplicate entries according to generic hash and equality comparisons on the entries. If an element occurs multiple times in the list then the later occurrences are discarded. The input list. The result list. let input = [6;1;2;3;1;4;5;5] input |> List.distinct Evaluates to [6; 1; 2; 3; 4; 5]. This is an O(n) operation, where n is the length of the list.
truncate: int -> 'T list -> 'T listReturns at most N elements in a new list. The maximum number of items to return. The input list. The result list. let inputs = ["a"; "b"; "c"; "d"] inputs |> List.truncate 2 Evaluates to ["a"; "b"] let inputs = ["a"; "b"; "c"; "d"] inputs |> List.truncate 6 Evaluates to ["a"; "b"; "c"; "d"] let inputs = ["a"; "b"; "c"; "d"] inputs |> List.truncate 0 Evaluates to the empty list. This is an O(n) operation, where n is the number of elements to return.
isEmpty: 'T list -> boolReturns true if the list contains no elements, false otherwise. The input list. True if the list is empty. [ ] |> List.isEmpty Evaluates to true [ "pear"; "banana" ] |> List.isEmpty Evaluates to false This is an O(1) operation.
succeed: 'value -> Flow<'env,'error,'value>Same as ok. The value to wrap in a successful flow. A flow that always succeeds with the provided value. let result = Flow.succeed 42 |> Flow.run () // result = Success 42
NoneThe representation of "No value"
union: Set<'T> -> Set<'T> -> Set<'T>Computes the union of the two sets. The first input set. The second input set. The union of set1 and set2. let set1 = Set.empty.Add(1).Add(2).Add(3) let set2 = Set.empty.Add(2).Add(3).Add(4) printfn $"The union of {set1} and {set2} is {(Set.union set1 set2)}" The sample evaluates to the following output: The union of set [1; 2; 3] and set [2; 3; 4] is set [1; 2; 3; 4] This is an O(m log n) operation, where m is the number of elements in the first set and n is the number of elements in the second set.
ofList: 'T list -> Set<'T>Builds a set that contains the same elements as the given list. The input list. A set containing the elements form the input list. let set = Set.ofList [1, 2, 3] printfn $"The set is {set} and type is {set.GetType().Name}" The sample evaluates to the following output: The set is set [(1, 2, 3)] and type is "FSharpSet`1" This is an O(n log n) operation, where n is the number of elements in the list.
remaining: string listexcept: 'T seq -> 'T list -> 'T listReturns a new list with the distinct elements of the input list which do not appear in the itemsToExclude sequence, using generic hash and equality comparisons to compare values. A sequence whose elements that also occur in the input list will cause those elements to be removed from the result. A list whose elements that are not also in itemsToExclude will be returned. A list that contains the distinct elements of list that do not appear in itemsToExclude. Thrown when itemsToExclude is null. let input = [1, "Kirk"; 2, "Spock"; 3, "Kenobi"] input |> List.except [3, "Kenobi"] Evaluates to [(1, "Kirk"); (2, "Spock")]. [0..10] |> List.except [1..5] // evaluates [0; 6; 7; 8; 9; 10] [1..5] |> List.except [0..10] // evaluates [] This is an O(n+m) operation, where n is the length of the list and m is the length of the items to exclude.
flow: FlowBuilderThe universal flow { } computation expression.
SomeThe representation of "Value of type 'T" The input value. An option representing the value.
(@): 'T list -> 'T list -> 'T listConcatenate two lists. The first list. The second list. The concatenation of the lists. let l1 = ['a'; 'b'; 'c'] let l2 = ['d'; 'e'; 'f'] l1 @ l2 // Evaluates to ['a'; 'b'; 'c'; 'd'; 'e'; 'f'] This is an O(n) operation, where n is the length of the first list.
collect: ('a -> FlowStream<'b,'c,'d>) -> FlowStream<'b,'c,'a> -> FlowStream<'b,'c,'d>Maps each value to a stream and concatenates the resulting streams. stream |> FlowStream.collect FlowStream.fromSeq
fromSeq: 'value seq -> FlowStream<'env,'error,'value>Creates a stream from a synchronous sequence of values. The sequence of values to be emitted by the stream. A that yields each value from the sequence. FlowStream.fromSeq [1..10] |> FlowStream.runCollect |> Flow.run ()
Starting from the home page, the crawl finds every page once, a frontier batch at a time:
crawl [ "/" ] |> FlowStream.map _.Url |> FlowStream.runCollect |> Flow.run (ClockEnvironment Clock.live)
|> shouldEqual (Exit.Success [ "/"; "/docs"; "/blog"; "/docs/flow" ])
crawl: string list -> FlowStream<ClockEnvironment,Never,HtmlPage>(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
Axial.FlowStreamModulemap: ('v -> 'w) -> FlowStream<'env,'error,'v> -> FlowStream<'env,'error,'w>Transforms the successful values of a stream using the provided function. The function to transform each value. The stream whose values should be transformed. A new stream that yields transformed values. let stream = FlowStream.fromSeq [1; 2; 3] |> FlowStream.map (fun n -> n * 2)
_arg1: HtmlPageUrl: stringrunCollect: FlowStream<'a,'b,'c> -> Flow<'a,'b,'c list>Collects all emitted values into a list. stream |> FlowStream.runCollect
Axial.Flowrun: 'env -> Flow<'env,'error,'value> -> Exit<'value,'error>Runs the workflow and blocks until the final exit is available. The environment used by the workflow. The workflow to run. The final workflow exit. let exit = workflow |> Flow.run environment
``.ctor``: IClock -> ClockEnvironmentAxial.PlatformService.ClockHelpers for the clock service.
live: IClockCreates a live clock backed by and a monotonic timer.
shouldEqual: 'a -> 'a -> unitAxial.Exit`2Represents the final outcome of a workflow execution. The type of the success value. The type of the domain-specific failure value.
SuccessThe workflow completed successfully.
One downstream pull fetches one frontier batch. Flow.sequencePar fetches the batch concurrently and preserves its URL
order. The total extractLinks function turns those pages into the next private frontier. FlowStream.collect then
flattens each emitted page batch so consumers see HtmlPage values rather than crawler state.
Backpressure applies between frontier batches: the crawler does not fetch the next discovered batch until downstream pulls again. A terminal consumer supplies the stream scope, so failure, interruption, or early termination cancels outstanding requests and closes their resources. This pattern also applies to paginated APIs, where the private state is a continuation token and each emitted value is a page or item.

