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.JavaScriptRunnable Examples
These examples are built and run while this page is generated, keeping the documentation tied to executable code.
Playground
Run it:
dotnet run --project examples/Axial.Playground/Axial.Playground.fsproj --nologo
Source: Program.fs
open System
open System.Threading
open System.Threading.Tasks
open Axial
type AppEnv =
{ Prefix: string
Name: string
LoadSuffix: Task<string> }
let greetingFlow : Flow<AppEnv, string, string> =
Flow.envWith (fun env -> $"{env.Prefix} {env.Name}") // Flow<AppEnv, string, string>
let greetingAsync : Flow<AppEnv, string, string> =
flow {
let! greeting = greetingFlow
let! (checkedGreeting: string) =
if String.IsNullOrWhiteSpace greeting then
Error "Blank greeting"
else
Ok greeting
return checkedGreeting.ToUpperInvariant()
}
let greetingTask : Flow<AppEnv, string, string> =
flow {
let! env = Flow.env // Flow<AppEnv, string, AppEnv>
let! greeting = greetingFlow // Flow<AppEnv, string, string>
let! suffix = Flow.awaitStartedTask env.LoadSuffix
return $"{greeting}{suffix}"
}
[<EntryPoint>]
let main _ =
let env =
{ Prefix = "Hello"
Name = "Ada"
LoadSuffix = Task.FromResult "!" }
let syncResult =
greetingFlow
|> fun workflow -> workflow |> Flow.run env
let asyncResult =
greetingAsync
|> fun workflow -> workflow |> Flow.run env
let taskResult =
greetingTask
|> fun workflow -> workflow |> Flow.run env
printfn "Flow: %A" syncResult
printfn "Async: %A" asyncResult
printfn "Task: %A" taskResult
// Flow: Ok "Hello Ada"
// Async: Ok "HELLO ADA"
// Task: Ok "Hello Ada!"
0
SystemThreadingTasksAxial13-testing_02-runnable-examples.md_page.AppEnvPrefix: stringstringAn abbreviation for the CLI type . Basic Types
Name: stringLoadSuffix: Task<string>System.Threading.Tasks.Task`1Represents an asynchronous operation that can return a value. The type of the result produced by this .
greetingFlow: Flow<AppEnv,string,string>Axial.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.FlowenvWith: ('env -> 'value) -> Flow<'env,'error,'value>Projects one value from the current environment. This is the primary way to access app dependencies, configuration, or request metadata stored in env. The projection runs only when the flow is executed, so constructing the flow is still pure and side-effect free. Prefer small projections over passing a large environment deeper into reusable helpers. A function that extracts a value from the environment. A containing the projected value. let currentTime () = Flow.envWith (fun (environment: BaseRuntime) -> environment.Clock.UtcNow())
env: AppEnvgreetingAsync: Flow<AppEnv,string,string>flow: FlowBuilderThe universal flow { } computation expression.
greeting: stringcheckedGreeting: stringSystem.StringRepresents text as a sequence of UTF-16 code units.
IsNullOrWhiteSpace: string -> boolIndicates whether a specified string is , empty, or consists only of white-space characters. The string to test. if the parameter is or , or if consists exclusively of white-space characters.
ErrorRepresents an Error or a Failure. The code failed with a value of 'TError representing what went wrong.
OkRepresents an OK or a Successful result. The code succeeded with a value of 'T.
ToUpperInvariant: unit -> stringReturns a copy of this object converted to uppercase using the casing rules of the invariant culture. The uppercase equivalent of the current string.
greetingTask: Flow<AppEnv,string,string>env: Flow<'env,'error,'env>Reads the current environment as the successful flow value. Use this when the next step genuinely needs the whole environment value, for example when passing a request context to another helper. For a single dependency or configuration value, prefer Flow.envWith; it keeps the dependency local and makes the workflow easier to scan. A whose successful value is the current environment. let myFlow = Flow.env |> Flow.map (fun env -> env)
suffix: stringawaitStartedTask: Task<'value> -> Flow<'env,'error,'value>Observes a task that has already been started. The operation is in flight before this is called. It therefore starts outside the workflow, ignores the runtime's cancellation token, and yields the same single result no matter how many times the flow is executed. Prefer fromTask, which keeps the flow cold. Thrown exceptions are recorded as defects (Cause.Die). A task that is already running. .NET only
Microsoft.FSharp.Core.EntryPointAttributeAdding this attribute to a function indicates it is the entrypoint for an application. If this attribute is not specified for an EXE then the initialization implicit in the module bindings in the last file in the compilation sequence are used as the entrypoint. Attributes
main: string array -> intSystem.Threading.Tasks.TaskRepresents an asynchronous operation.
FromResult: 'TResult -> Task<'TResult>Creates a that's completed successfully with the specified result. The result to store into the completed task. The type of the result returned by the task. The successfully completed task.
syncResult: Exit<string,string>(|>): '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
workflow: Flow<AppEnv,string,string>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
asyncResult: Exit<string,string>taskResult: Exit<string,string>printfn: Printf.TextWriterFormat<'T> -> 'TPrint to stdout using the given format, and add a newline. The formatter. The formatted result. See Printf.printfn (link: ) for examples.
Flow: Success "Hello Ada"
Async: Success "HELLO ADA"
Task: Success "Hello Ada!"
Maintenance patterns
Run it:
dotnet run --project examples/Axial.MaintenanceExamples/Axial.MaintenanceExamples.fsproj --nologo
Source: Program.fs
open System
open System.Threading
open System.Threading.Tasks
open Axial
let runFlow label env (workflow: Flow<'env, 'error, 'value>) =
let result = workflow |> Flow.run env
printfn "%s: %A" label result
let runAsyncExample label env (workflow: Flow<'env, 'error, 'value>) =
let result =
workflow
|> fun workflow -> workflow |> Flow.run env
printfn "%s: %A" label result
let runTaskExample label env (workflow: Flow<'env, 'error, 'value>) =
let result =
workflow
|> fun workflow -> workflow |> Flow.run env
printfn "%s: %A" label result
let syncExample : Flow<int, string, int> =
Flow.envWith id // Flow<int, string, int>
|> Flow.map ((+) 1)
let asyncExample : Flow<int, string, int> =
flow {
let! value = async { return 21 }
return value * 2
}
let taskExample : Flow<int, string, int> =
flow {
let! env = Flow.envWith id
let! suffix = ColdTask(fun _ -> Task.FromResult 5)
return env + suffix
}
[<EntryPoint>]
let main _ =
runFlow "Flow" 20 syncExample
runAsyncExample "Async" 20 asyncExample
runTaskExample "Task" 20 taskExample
// Flow: Ok 21
// Async: Ok 42
// Task: Ok 25
0Flow: Success 21
Async: Success 42
Task: Success 25
Supervision and fiber observability
Run it:
dotnet run --project examples/Axial.Examples/Axial.Examples.fsproj --nologo
Source: SupervisionExample.fs
module SupervisionExample
open System
open Axial
// Demonstrates defect supervision and fiber observability:
// 1. Flow.Runtime.supervise restarts background work that dies with a defect.
// 2. A FiberObserver installed once at the edge reports defects from fibers nobody awaited.
// 3. Flow.forkDetached states intentional fire-and-forget at the call site, silencing the report.
let private flakyWorker (attempts: int ref) : Flow<unit, string, string> =
Flow.delay(fun () ->
attempts.Value <- attempts.Value + 1
if attempts.Value < 3 then
// A bug, not a typed domain error: supervise restarts these.
Flow.die (InvalidOperationException $"worker crashed on attempt {attempts.Value}")
else
Flow.succeed $"worker succeeded on attempt {attempts.Value}")
let private consoleObserver =
{ FiberObserver.none with
OnEnd = fun metadata defect ->
match defect with
| Some exn -> printfn $" [observer] fiber {metadata.Id.Value} died: {exn.Message}"
| None -> printfn $" [observer] fiber {metadata.Id.Value} ended: {metadata.Status}"
OnUnobservedDefect = fun metadata defect ->
let source =
match metadata with
| Some m -> $"fiber {m.Id.Value}"
| None -> "race/timeout loser"
printfn $" [observer] UNOBSERVED DEFECT from {source}: {defect.Message}" }
let private supervisedRecovery () =
printfn "-- Flow.Runtime.supervise: restart a background worker that dies with a defect"
let attempts = ref 0
let policy : SupervisePolicy =
{ MaxAttempts = 5
Delay = fun _ -> TimeSpan.Zero
ShouldRestart = fun _ -> true }
let result =
flakyWorker attempts
|> Flow.Runtime.supervise policy
|> Flow.run ()
printfn $" result after {attempts.Value} attempts: %A{result}"
let private unobservedDefectReporting () =
printfn "-- FiberObserver: a discarded fork handle whose fiber dies is reported"
let workflow =
flow {
// The handle is deliberately discarded: without an observer this crash is silent.
let! _fiber = Flow.fork (Flow.die (InvalidOperationException "background job blew up") : Flow<unit, string, int>)
do! Flow.Runtime.sleep (TimeSpan.FromMilliseconds 50.0)
return "main workflow finished fine"
}
|> Flow.withFiberObserver consoleObserver
let result = workflow |> Flow.run ()
printfn $" result: %A{result}"
let private intentionalFireAndForget () =
printfn "-- Flow.forkDetached: intentional fire-and-forget is not reported as unobserved"
let workflow =
flow {
let! _fiber = Flow.forkDetached (Flow.die (InvalidOperationException "best-effort work failed") : Flow<unit, string, int>)
do! Flow.Runtime.sleep (TimeSpan.FromMilliseconds 50.0)
return "no unobserved-defect report for detached work"
}
|> Flow.withFiberObserver consoleObserver
let result = workflow |> Flow.run ()
printfn $" result: %A{result}"
let run () =
printfn "=== Supervision and fiber observability ==="
supervisedRecovery ()
unobservedDefectReporting ()
intentionalFireAndForget ()Flow result: Success { Id = 42
Name = "Ada" }
Flow result: Success "Hello [11111111-1111-1111-1111-111111111111] Ada"
Flow result: Success "Hello [11111111-1111-1111-1111-111111111111] Ada!"
Policy examples
accepted: Success { Sku = "SKU-1"
Quantity = 3 }
rejected (not int): Failure (Fail QuantityNotANumber)
rejected (zero): Failure (Fail QuantityNotPositive)
rejected (over cap): Failure (Fail (QuantityOverCap 10))
cap disabled: Success { Sku = "SKU-1"
Quantity = 50 }
=== Supervision and fiber observability ===
-- Flow.Runtime.supervise: restart a background worker that dies with a defect
result after 3 attempts: Success "worker succeeded on attempt 3"
-- FiberObserver: a discarded fork handle whose fiber dies is reported
[observer] fiber N died: background job blew up
[observer] UNOBSERVED DEFECT from fiber N: background job blew up
result: Success "main workflow finished fine"
-- Flow.forkDetached: intentional fire-and-forget is not reported as unobserved
[observer] fiber N died: best-effort work failed
result: Success "no unobserved-defect report for detached work"

