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.NET Hosting
Axial.Hosting connects root Flow applications to .NET process and Generic Host lifecycle. It also adapts
Microsoft.Extensions.Logging to the explicit ILog service and provides fiber-defect logging.
dotnet add package Axial.Hosting
The package is optional. App works in console, desktop, test, and embedded applications without
Microsoft.Extensions.Hosting or a dependency-injection container.
Standalone CLI or Script
Use DotNetApp.run when the application owns a console process but does not use Generic Host:
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.
open Axial
open Axial.Hosting
type AppError = | InvalidArguments of string
let describeError = function
| InvalidArguments message -> message
let application : Flow<string array, AppError, unit> =
flow {
let! args = Flow.env<string array, AppError>
if Array.isEmpty args then
return! Flow.fail (InvalidArguments "Supply at least one argument.")
}
// In a program, mark this [<EntryPoint>].
let main args =
(DotNetApp.run describeError args application).GetAwaiter().GetResult()
AxialHostingCore_12-platforms-and-hosting_06-dotnet.md_page.AppErrorInvalidArgumentsstringAn abbreviation for the CLI type . Basic Types
describeError: AppError -> stringmessage: stringapplication: Flow<string array,AppError,unit>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.
arraySingle dimensional, zero-based arrays, written int array, string array etc. Use the values in the module to manipulate values of this type, or the notation arr.[x] to get/set array values. Basic Types
unitThe type 'unit', which has only one value "()". This value is special and always uses the representation 'null'. Basic Types
flow: FlowBuilderThe universal flow { } computation expression.
args: string arrayAxial.Flowenv: 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)
Microsoft.FSharp.Collections.ArrayModuleContains operations for working with arrays. See also F# Language Guide - Arrays.
isEmpty: 'T array -> boolReturns true if the given array is empty, otherwise false. The input array. True if the array is empty. Thrown when the input array is null. [| |] |> Array.isEmpty Evaluates to true [| "pear"; "banana" |] |> Array.isEmpty Evaluates to false This is an O(1) operation.
fail: 'error -> Flow<'env,'error,'value>Same as error. The error value to wrap in a failing flow. A flow that always fails with the provided error. let result = Flow.fail "error" |> Flow.run () // result = Failure (Cause.Fail "error")
main: string array -> intGetAwaiter: unit -> Runtime.CompilerServices.TaskAwaiter<int>Gets an awaiter used to await this . An awaiter instance.
Axial.Hosting.DotNetAppStandalone .NET process integration for Flow applications that do not use Generic Host.
run: ('error -> string) -> 'env -> Flow<'env,'error,'value> -> Task<int>Runs a standalone application, translating Ctrl+C into coordinated stop and returning a process exit code.
GetResult: unit -> intEnds the wait for the completion of the asynchronous task. The object was not properly initialized. The task was canceled. The task completed in a state. The result of the completed task.
main [| "orders.csv" |] |> shouldEqual 0
main [||] |> shouldEqual 1
main: string array -> int(|>): '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
shouldEqual: 'a -> 'a -> unitDotNetApp.run installs a temporary Console.CancelKeyPress handler. Ctrl+C requests App.Stop(), waits for root
scope cleanup, removes the handler, and returns:
| Exit | Process code |
|---|---|
| Success | 0 |
| Typed failure only | 1 |
| Defect | 2 |
| Interruption | 130 |
A cleanup defect takes precedence when a cause also contains interruption.
It returns the code rather than calling Environment.Exit, so finally blocks and asynchronous finalizers are not
skipped.
Generic Host
Build the application as a Flow whose input is either IServiceProvider or an explicit environment constructed
from it:
open Microsoft.Extensions.DependencyInjection
open Microsoft.Extensions.Hosting
open Axial
open Axial.Hosting
let builder = Host.CreateApplicationBuilder(args)
builder.Services.AddSingleton<IOrderRepository, SqlOrderRepository>()
|> ignore
let application : Flow<AppEnv, AppError, unit> =
program
|> Layer.provide Live.appLayer
builder.Services
|> Hosting.addApp
(fun services ->
{ Orders = services.GetRequiredService<IOrderRepository>() })
AppError.describe
application
|> ignore
builder.Build().Run()The registered FlowHostedService:
- Constructs the explicit environment when the host starts.
- Starts one root
App. - Connects
IHostApplicationLifetime.ApplicationStoppingto coordinated stop. - Logs success, typed failure, interruption, or defects through the host logger.
- Requests host shutdown when the finite root application completes.
- Waits for root cleanup from
StopAsync.
Use Hosting.addAppWith { StopHostOnCompletion = false } when the root Flow is one hosted participant and another
hosted service owns process completion.
IServiceProvider stays at the application edge. Prefer constructing an explicit record or providing a Layer before
domain workflows run. See Providing the environment.
Microsoft Logging as ILog
Create the explicit Axial logging service from an existing logger:
let axialLogFor (logger: Microsoft.Extensions.Logging.ILogger) : ILog =
MicrosoftLogging.create logger
axialLogFor: Extensions.Logging.ILogger -> ILoglogger: Extensions.Logging.ILoggerMicrosoftMicrosoft.Extensions.Logging.ILoggerRepresents a type used to perform logging. Aggregates most logging patterns to a single method.
ExtensionsLoggingAxial.PlatformService.ILogProvides synchronous access to workflow logging as an explicit service.
Axial.Hosting.MicrosoftLoggingAdapts Microsoft.Extensions.Logging loggers to Axial's explicit logging service.
create: Extensions.Logging.ILogger -> ILogCreates an Axial logger backed by a supplied Microsoft logger.
Or choose a category through a factory:
let axialLogFrom (loggerFactory: Microsoft.Extensions.Logging.ILoggerFactory) : ILog =
MicrosoftLogging.fromFactory "MyApp" loggerFactory
axialLogFrom: Extensions.Logging.ILoggerFactory -> ILogloggerFactory: Extensions.Logging.ILoggerFactoryMicrosoftMicrosoft.Extensions.Logging.ILoggerFactoryRepresents a type used to configure the logging system and create instances of from the registered s.
ExtensionsLoggingAxial.PlatformService.ILogProvides synchronous access to workflow logging as an explicit service.
Axial.Hosting.MicrosoftLoggingAdapts Microsoft.Extensions.Logging loggers to Axial's explicit logging service.
fromFactory: string -> Extensions.Logging.ILoggerFactory -> ILogCreates an Axial logger with an explicit Microsoft logging category.
MicrosoftLogging.layer "MyApp" provisions ILog from an ILoggerFactory layer input. All Axial log levels and
exception objects are preserved. The adapter never silently substitutes a no-op logger.
Fiber Defect Logging
Install the observer once around the root application:
let observed (logger: Microsoft.Extensions.Logging.ILogger) (application: Flow<unit, string, unit>) =
application
|> FiberLogging.observe logger
observed: Extensions.Logging.ILogger -> Flow<unit,string,unit> -> Flow<unit,string,unit>logger: Extensions.Logging.ILoggerMicrosoftMicrosoft.Extensions.Logging.ILoggerRepresents a type used to perform logging. Aggregates most logging patterns to a single method.
ExtensionsLoggingapplication: Flow<unit,string,unit>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.
unitThe type 'unit', which has only one value "()". This value is special and always uses the representation 'null'. Basic Types
stringAn abbreviation for the CLI type . Basic Types
(|>): '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.Hosting.FiberLoggingFiber-lifecycle logging through Microsoft.Extensions.Logging.
observe: Extensions.Logging.ILogger -> Flow<'env,'error,'value> -> Flow<'env,'error,'value>Installs fiber defect logging at the root application edge.
Fiber defects are errors; unobserved fiber defects are critical entries. Compose it with telemetry when both are required:
let observedWithSpans (logger: Microsoft.Extensions.Logging.ILogger) (application: Flow<unit, string, unit>) =
application
|> Flow.withFiberObserver
(FiberObserver.compose
FiberTelemetry.observerWithSpans
(FiberLogging.observer logger))Logging is an explicit application dependency; telemetry remains runtime instrumentation. See Observability.
Desktop and Embedded Applications
Desktop frameworks already own application lifetime. Start App after startup and await stop from the framework's
closing path:
let startDesktop (requestExit: unit -> unit) (application: Flow<unit, string, unit>) =
let running = App.start () application
let closeApplication () = async {
let! _ = running.Stop()
requestExit ()
}
closeApplication
startDesktop: (unit -> unit) -> Flow<unit,string,unit> -> unit -> Async<unit>requestExit: unit -> unitunitThe type 'unit', which has only one value "()". This value is special and always uses the representation 'null'. Basic Types
application: Flow<unit,string,unit>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.
stringAn abbreviation for the CLI type . Basic Types
running: AppHandle<string,unit>Axial.AppStarts and controls root Flow applications without requiring an external hosting framework.
start: 'env -> Flow<'env,'error,'value> -> AppHandle<'error,'value>Starts a root workflow and returns a handle that owns its lifetime. The explicit environment supplied to the root workflow. The root workflow to start. A handle for observing completion or requesting coordinated stop. Fable compatible
closeApplication: unit -> Async<unit>async: AsyncBuilderBuilds an asynchronous workflow using computation expression syntax. let sleepExample() = async { printfn "sleeping" do! Async.Sleep 10 printfn "waking up" return 6 } sleepExample() |> Async.RunSynchronously
Stop: unit -> Async<Exit<unit,string>>Requests cooperative interruption and waits for the final application exit.
Do not block the UI thread on Completion. Framework-specific packages are unnecessary unless an integration can
provide more than wiring one close event to Stop().
Other Runtimes
- Node hosting handles Node arguments,
process.env, signals, and exit status. - Browser hosting handles explicit UI ownership and
AbortSignal.

