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.JavaScriptComposing Layers
A Layer<'input, 'error, 'output> builds an environment or service bundle from an input value. It runs inside a
Scope, so resources acquired during provisioning can be finalized when the provided flow finishes.
Show page setup
// Setup for the checked examples on this page.
open System
open Axial
open Axial.Layers
open Axial.PlatformService
/// 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 actualopen Axial
open Axial.Layers
AxialLayersThe examples on this page provision an orders repository from a settings map. Opening the repository is recorded in
events, so each example can check what was opened and closed:
type Config = { ConnectionString: string }
type StartupError = MissingSetting of string
type IOrderRepository =
abstract Count: unit -> int
type AppEnv =
{ Config: Config
Orders: IOrderRepository
Clock: IClock }
let events = ResizeArray<string>()
let fail (error: StartupError) : Layer<'input, StartupError, 'output> =
Layer.fromFlow (Flow.fail error)
let configLayer : Layer<Map<string, string>, StartupError, Config> =
Layer.envWith (Map.tryFind "orders")
|> Layer.bind (function
| Some connectionString -> Layer.succeed { ConnectionString = connectionString }
| None -> fail (MissingSetting "orders"))
let openOrders (config: Config) : Flow<Map<string, string>, StartupError, IOrderRepository> =
Flow.delay (fun () ->
events.Add $"opened {config.ConnectionString}"
Flow.ok { new IOrderRepository with member _.Count() = 3 })
let closeOrders (config: Config) (_: IOrderRepository) : Flow<Map<string, string>, Never, unit> =
Flow.delay (fun () ->
events.Add $"closed {config.ConnectionString}"
Flow.ok ())
let ordersLayerFromConfig (config: Config) : Layer<Map<string, string>, StartupError, IOrderRepository> =
Layer.fromFlow (Flow.scopeAcquireRelease (openOrders config) (closeOrders config))
let clockLayer : Layer<Map<string, string>, StartupError, IClock> =
Layer.succeed (Clock.fromValue (DateTimeOffset(2026, 1, 1, 0, 0, 0, TimeSpan.Zero)))
let countOrders : Flow<AppEnv, StartupError, string> =
Flow.envWith (fun env -> $"{env.Orders.Count()} orders in {env.Config.ConnectionString}")
FsLiveDocsGeneratedPage7_F9357565CFEE.ConfigConnectionString: stringstringAn abbreviation for the CLI type . Basic Types
FsLiveDocsGeneratedPage7_F9357565CFEE.StartupErrorMissingSettingFsLiveDocsGeneratedPage7_F9357565CFEE.IOrderRepositoryCount: IOrderRepository -> unit -> intunitThe type 'unit', which has only one value "()". This value is special and always uses the representation 'null'. Basic Types
intAn abbreviation for the CLI type . Basic Types
FsLiveDocsGeneratedPage7_F9357565CFEE.AppEnvConfig: ConfigOrders: IOrderRepositoryClock: IClockAxial.IClockSupplies wall time, monotonic time, and cancellable delays from one source. Wall time is for timestamps. Use differences between Elapsed readings for durations.
events: ResizeArray<string>``.ctor``: unit -> unitInitializes a new instance of the class that is empty and has the default initial capacity.
fail: StartupError -> Layer<'input,StartupError,'output>error: StartupErrorAxial.Layers.Layer`3Represents a provisioning step that builds an explicit environment inside a scope. The input environment required to build the layer. The typed failure produced during provisioning. The environment or service bundle produced by the layer.
inputoutputAxial.Layers.LayerModulefromFlow: Flow<'input,'error,'output> -> Layer<'input,'error,'output>Creates a layer that builds its output by running a flow with the layer's input as its environment. The flow runs in the layer's scope, so what it registers with Flow.scopeAcquireRelease, Flow.scopeFinalizer, or Flow.scopeResource stays open until the Layer.provide boundary closes, and is released in reverse acquisition order. A typed failure fails provisioning; whatever was acquired before it is released. The flow that builds the layer output. A layer that succeeds with the flow's value. let database : Layer<AppConfig, DbError, Database> = Layer.fromFlow (Flow.scopeAcquireRelease (Database.connect ()) Database.close)
Axial.Flowfail: '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")
configLayer: Layer<Map<string,string>,StartupError,Config>Microsoft.FSharp.Collections.FSharpMap`2Immutable maps based on binary trees, where keys are ordered by F# generic comparison. By default comparison is the F# structural comparison function or uses implementations of the IComparable interface on key values. See the module for further operations on maps. All members of this class are thread-safe and may be used concurrently from multiple threads.
envWith: ('input -> 'output) -> Layer<'input,'error,'output>Projects part of the input environment into the layer output.
Microsoft.FSharp.Collections.MapModuleContains operations for working with values of type .
tryFind: 'Key -> Map<'Key,'T> -> 'T optionLookup an element in the map, returning a Some value if the element is in the domain of the map and None if not. The input key. The input map. The found Some value or None. 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.tryFind 1 // evaluates to Some "a" sample |> Map.tryFind 3 // evaluates to None
(|>): '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
bind: ('output -> Layer<'input,'error,'next>) -> Layer<'input,'error,'output> -> Layer<'input,'error,'next>Sequences layer provisioning with a dependent follow-up layer.
SomeThe representation of "Value of type 'T" The input value. An option representing the value.
connectionString: stringsucceed: 'output -> Layer<'input,'error,'output>Creates a layer that succeeds with a fixed output value.
NoneThe representation of "No value"
openOrders: Config -> Flow<Map<string,string>,StartupError,IOrderRepository>config: ConfigAxial.Flow`3delay: (unit -> Flow<'env,'error,'value>) -> Flow<'env,'error,'value>Defers flow construction until execution time. A function that returns the flow to execute. Recursive calls deferred here are consumed by the executor without growing a chain of parent tasks. Work that must run after the recursive call still needs storage until that call finishes. A flow that lazily evaluates the factory when executed. let flow = Flow.delay (fun () -> Flow.succeed 42)
Add: string -> unitAdds an object to the end of the . The object to be added to the end of the . The value can be for reference types.
ok: 'value -> Flow<'env,'error,'value>Creates a successful synchronous flow. The value to wrap in a successful flow. A flow that always succeeds with the provided value.
_: IOrderRepositoryCount: unit -> intcloseOrders: Config -> IOrderRepository -> Flow<Map<string,string>,Never,unit>Axial.NeverRepresents an error channel that cannot occur.
ordersLayerFromConfig: Config -> Layer<Map<string,string>,StartupError,IOrderRepository>scopeAcquireRelease: Flow<'env,'error,'resource> -> ('resource -> Flow<'env,Never,unit>) -> Flow<'env,'error,'resource>Acquires a value and registers its release with the current runtime scope. Same as Resource.create acquire release |> Flow.scopeResource; see Resource.create for how the release runs. The flow that acquires the value. The release flow run when the current scope closes. A flow that succeeds with the acquired value.
clockLayer: Layer<Map<string,string>,StartupError,IClock>Axial.PlatformService.ClockHelpers for the clock service.
fromValue: DateTimeOffset -> IClockCreates a deterministic clock that always returns the supplied instant; measured durations are zero.
``.ctor``: int * int * int * int * int * int * TimeSpan -> unitInitializes a new instance of the structure using the specified year, month, day, hour, minute, second, and offset. The year (1 through 9999). The month (1 through 12). The day (1 through the number of days in ). The hours (0 through 23). The minutes (0 through 59). The seconds (0 through 59). The time's offset from Coordinated Universal Time (UTC). does not represent whole minutes. is less than one or greater than 9999. -or- is less than one or greater than 12. -or- is less than one or greater than the number of days in . -or- is less than zero or greater than 23. -or- is less than 0 or greater than 59. -or- is less than 0 or greater than 59. -or- is less than -14 hours or greater than 14 hours. -or- The property is earlier than or later than .
System.TimeSpanRepresents a time interval.
Zero: TimeSpanRepresents the zero value. This field is read-only.
countOrders: Flow<AppEnv,StartupError,string>envWith: ('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: AppEnvRequires & types
Requires: open Axial; open Axial.Layers; open Axial.PlatformService; open System (packages: Axial, Axial.Layers, Axial.PlatformService)val events: ResizeArray<string>val fail: StartupError -> Layer<'input,StartupError,'output>val configLayer: Layer<Map<string,string>,StartupError,Config>val openOrders: Config -> Flow<Map<string,string>,StartupError,IOrderRepository>val closeOrders: Config -> IOrderRepository -> Flow<Map<string,string>,Never,unit>val ordersLayerFromConfig: Config -> Layer<Map<string,string>,StartupError,IOrderRepository>val clockLayer: Layer<Map<string,string>,StartupError,IClock>val countOrders: Flow<AppEnv,StartupError,string>Primary Shape
Use layer { } for application environment construction.
let! binds a layer's output to the name on its left. do! binds a layer returning unit, while return! uses
another layer as the block's result. Sibling and! bindings build independent layers in parallel.
let appLayer : Layer<Map<string, string>, StartupError, AppEnv> =
layer {
let! config = configLayer
let! orders = ordersLayerFromConfig config
and! clock = clockLayer
return { Config = config; Orders = orders; Clock = clock }
}
appLayer: Layer<Map<string,string>,StartupError,AppEnv>Axial.Layers.Layer`3Represents a provisioning step that builds an explicit environment inside a scope. The input environment required to build the layer. The typed failure produced during provisioning. The environment or service bundle produced by the layer.
Microsoft.FSharp.Collections.FSharpMap`2Immutable maps based on binary trees, where keys are ordered by F# generic comparison. By default comparison is the F# structural comparison function or uses implementations of the IComparable interface on key values. See the module for further operations on maps. All members of this class are thread-safe and may be used concurrently from multiple threads.
stringAn abbreviation for the CLI type . Basic Types
FsLiveDocsGeneratedPage7_F9357565CFEE.StartupErrorFsLiveDocsGeneratedPage7_F9357565CFEE.AppEnvlayer: LayerBuilderThe layer { } computation expression for provisioning explicit service environments.
config: ConfigconfigLayer: Layer<Map<string,string>,StartupError,Config>orders: IOrderRepositoryordersLayerFromConfig: Config -> Layer<Map<string,string>,StartupError,IOrderRepository>clock: IClockclockLayer: Layer<Map<string,string>,StartupError,IClock>Config: ConfigOrders: IOrderRepositoryClock: IClockRequires & types
Requires: open Axial.Layers (packages: Axial.Layers)val appLayer: Layer<Map<string,string>,StartupError,AppEnv>The left- and right-hand types can be annotated:
let appLayerAnnotated =
layer {
let! (config: Config) = (configLayer: Layer<Map<string, string>, StartupError, Config>)
let! (orders: IOrderRepository) =
(ordersLayerFromConfig config: Layer<Map<string, string>, StartupError, IOrderRepository>)
and! (clock: IClock) = (clockLayer: Layer<Map<string, string>, StartupError, IClock>)
return { Config = config; Orders = orders; Clock = clock }
}
// Layer<Map<string, string>, StartupError, AppEnv>
appLayerAnnotated: Layer<Map<string,string>,StartupError,AppEnv>layer: LayerBuilderThe layer { } computation expression for provisioning explicit service environments.
config: ConfigFsLiveDocsGeneratedPage7_F9357565CFEE.ConfigconfigLayer: Layer<Map<string,string>,StartupError,Config>Axial.Layers.Layer`3Represents a provisioning step that builds an explicit environment inside a scope. The input environment required to build the layer. The typed failure produced during provisioning. The environment or service bundle produced by the layer.
Microsoft.FSharp.Collections.FSharpMap`2Immutable maps based on binary trees, where keys are ordered by F# generic comparison. By default comparison is the F# structural comparison function or uses implementations of the IComparable interface on key values. See the module for further operations on maps. All members of this class are thread-safe and may be used concurrently from multiple threads.
stringAn abbreviation for the CLI type . Basic Types
FsLiveDocsGeneratedPage7_F9357565CFEE.StartupErrororders: IOrderRepositoryFsLiveDocsGeneratedPage7_F9357565CFEE.IOrderRepositoryordersLayerFromConfig: Config -> Layer<Map<string,string>,StartupError,IOrderRepository>clock: IClockAxial.IClockSupplies wall time, monotonic time, and cancellable delays from one source. Wall time is for timestamps. Use differences between Elapsed readings for durations.
clockLayer: Layer<Map<string,string>,StartupError,IClock>Config: ConfigOrders: IOrderRepositoryClock: IClockRequires & types
Requires: open Axial; open Axial.Layers (packages: Axial, Axial.Layers)val appLayerAnnotated: Layer<Map<string,string>,StartupError,AppEnv>Layer.provide builds the layer, runs the workflow in the result, and releases what the layer acquired:
events.Clear()
countOrders
|> Layer.provide appLayer
|> Flow.run (Map [ "orders", "db://orders" ])
|> shouldEqual (Exit.Success "3 orders in db://orders")
List.ofSeq events |> shouldEqual [ "opened db://orders"; "closed db://orders" ]
countOrders
|> Layer.provide appLayer
|> Flow.run Map.empty
|> shouldEqual (Exit.Failure(Cause.Fail(MissingSetting "orders")))
events: ResizeArray<string>Clear: unit -> unitRemoves all elements from the .
countOrders: Flow<AppEnv,StartupError,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
Axial.Layers.LayerModuleprovide: Layer<'input,'error,'environment> -> Flow<'environment,'error,'value> -> Flow<'input,'error,'value>Builds an environment with a layer, runs a downstream flow, and always closes the layer scope. This is the provisioning boundary. It creates a fresh scope, builds the supplied layer inside that scope, runs the downstream flow with the built environment, and finalizes all acquired resources when the downstream flow completes or fails. The layer that builds the downstream environment. The flow to run with the provided environment. A flow that requires only the input environment of the layer. let program () = let runtimeLayer = Layer.succeed "production" let workflow = Flow.envWith String.length Layer.provide runtimeLayer workflow
appLayer: Layer<Map<string,string>,StartupError,AppEnv>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``: (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")]
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.
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.
ofSeq: 'T seq -> 'T listBuilds a new list from the given enumerable object. The input sequence. The list of elements from the sequence. let inputs = seq { 1; 2; 5 } inputs |> List.ofSeq Evaluates to [ 1; 2; 5 ]. This is an O(n) operation, where n is the length of the sequence.
Microsoft.FSharp.Collections.MapModuleContains operations for working with values of type .
empty: Map<'Key,'T>The empty map. This is an O(1) operation. let emptyMap = Map.empty<int, string>
FailureThe workflow failed due to a specific cause.
Axial.Cause`1Represents the cause of a failed workflow. The type of the domain-specific failure value.
FailAn expected domain-specific failure.
MissingSettingRequires & types
Requires: open Axial; open Axial.Layers (packages: Axial, Axial.Layers)Plain let! is sequential and dependent. Sibling and! bindings are independent and use Layer.merge, which provisions
branches in parallel through child scopes. An and! sibling cannot depend on a value introduced by another sibling;
that is an ordinary F# compile-time scope error, and the fix is to move it into a prior let!.
Layer functions
layer { } uses let! (via Layer.bind) and and! (via Layer.merge/Layer.zipPar) above; the full combinator
inventory — succeed, envWith, fromFlow, acquireRelease, addFinalizer, map/mapError/widenError,
zip, zipPar/merge, and map2/map3 — is in the
Layer API reference.
Layer error types must match the flow error type. When provisioning steps use different errors, map them into one
startup error type with Layer.mapError before calling Layer.provide. A layer that cannot fail, such as
BaseRuntime.live or Clock.layer, has error type Never; Layer.widenError gives it any error type.
type OrdersError = MissingOrders of string
type ClockError = BrokenClock of string
type ProvisionError =
| OrdersProblem of OrdersError
| ClockProblem of ClockError
let ordersFrom (config: string option) : Layer<unit, OrdersError, int> =
match config with
| Some _ -> Layer.succeed 3
| None -> Layer.fromFlow (Flow.fail (MissingOrders "orders"))
let clockFrom : Layer<unit, Never, IClock> =
Layer.succeed (Clock.fromValue (DateTimeOffset(2026, 1, 1, 0, 0, 0, TimeSpan.Zero)))
// Map the typed-error union into the shared union and widen the Never layer to the same error type.
let provision : Layer<unit, ProvisionError, int * IClock> =
Layer.zip
(ordersFrom None |> Layer.mapError OrdersProblem)
(clockFrom |> Layer.widenError)
FsLiveDocsGeneratedPage7_F9357565CFEE.OrdersErrorMissingOrdersstringAn abbreviation for the CLI type . Basic Types
FsLiveDocsGeneratedPage7_F9357565CFEE.ClockErrorBrokenClockFsLiveDocsGeneratedPage7_F9357565CFEE.ProvisionErrorOrdersProblemClockProblemordersFrom: string option -> Layer<unit,OrdersError,int>config: string optionoptionThe type of optional values. When used from other CLI languages the empty option is the null value. Use the constructors Some and None to create values of this type. Use the values in the Option module to manipulate values of this type, or pattern match against the values directly. 'None' values will appear as the value null to other CLI languages. Instance methods on this type will appear as static methods to other CLI languages due to the use of null as a value representation. Options
Axial.Layers.Layer`3Represents a provisioning step that builds an explicit environment inside a scope. The input environment required to build the layer. The typed failure produced during provisioning. The environment or service bundle produced by the layer.
unitThe type 'unit', which has only one value "()". This value is special and always uses the representation 'null'. Basic Types
intAn abbreviation for the CLI type . Basic Types
SomeThe representation of "Value of type 'T" The input value. An option representing the value.
Axial.Layers.LayerModulesucceed: 'output -> Layer<'input,'error,'output>Creates a layer that succeeds with a fixed output value.
NoneThe representation of "No value"
fromFlow: Flow<'input,'error,'output> -> Layer<'input,'error,'output>Creates a layer that builds its output by running a flow with the layer's input as its environment. The flow runs in the layer's scope, so what it registers with Flow.scopeAcquireRelease, Flow.scopeFinalizer, or Flow.scopeResource stays open until the Layer.provide boundary closes, and is released in reverse acquisition order. A typed failure fails provisioning; whatever was acquired before it is released. The flow that builds the layer output. A layer that succeeds with the flow's value. let database : Layer<AppConfig, DbError, Database> = Layer.fromFlow (Flow.scopeAcquireRelease (Database.connect ()) Database.close)
Axial.Flowfail: '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")
clockFrom: Layer<unit,Never,IClock>Axial.NeverRepresents an error channel that cannot occur.
Axial.IClockSupplies wall time, monotonic time, and cancellable delays from one source. Wall time is for timestamps. Use differences between Elapsed readings for durations.
Axial.PlatformService.ClockHelpers for the clock service.
fromValue: DateTimeOffset -> IClockCreates a deterministic clock that always returns the supplied instant; measured durations are zero.
``.ctor``: int * int * int * int * int * int * TimeSpan -> unitInitializes a new instance of the structure using the specified year, month, day, hour, minute, second, and offset. The year (1 through 9999). The month (1 through 12). The day (1 through the number of days in ). The hours (0 through 23). The minutes (0 through 59). The seconds (0 through 59). The time's offset from Coordinated Universal Time (UTC). does not represent whole minutes. is less than one or greater than 9999. -or- is less than one or greater than 12. -or- is less than one or greater than the number of days in . -or- is less than zero or greater than 23. -or- is less than 0 or greater than 59. -or- is less than 0 or greater than 59. -or- is less than -14 hours or greater than 14 hours. -or- The property is earlier than or later than .
System.TimeSpanRepresents a time interval.
Zero: TimeSpanRepresents the zero value. This field is read-only.
provision: Layer<unit,ProvisionError,(int * IClock)>zip: Layer<'input,'error,'left> -> Layer<'input,'error,'right> -> Layer<'input,'error,('left * 'right)>Builds two layers from the same input and scope and returns both outputs. zip is sequential: the left layer is provisioned before the right layer. Use zipPar or merge for independent parallel provisioning.
(|>): '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
mapError: ('error -> 'nextError) -> Layer<'input,'error,'output> -> Layer<'input,'nextError,'output>Maps the typed provisioning failure of a layer.
widenError: Layer<'input,Never,'output> -> Layer<'input,'error,'output>Gives a layer that cannot fail with a typed error any error type. Layer.provide and the layer combinators require one error type. Widen a layer that cannot fail, such as BaseRuntime.live, to combine it with layers or workflows that can. let runtime : Layer<unit, string, int> = Layer.succeed 1 |> Layer.widenError
Requires & types
Requires: open Axial; open Axial.Layers; open Axial.PlatformService; open System (packages: Axial, Axial.Layers, Axial.PlatformService)val ordersFrom: string option -> Layer<unit,OrdersError,int>val clockFrom: Layer<unit,Never,IClock>val provision: Layer<unit,ProvisionError,(int * IClock)>Flow.ok 0
|> Layer.provide (provision |> Layer.map ignore)
|> Flow.run ()
|> shouldEqual (Exit.Failure(Cause.Fail(OrdersProblem(MissingOrders "orders"))))
Axial.Flowok: 'value -> Flow<'env,'error,'value>Creates a successful synchronous flow. The value to wrap in a successful flow. A flow that always succeeds with the provided value.
(|>): '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.Layers.LayerModuleprovide: Layer<'input,'error,'environment> -> Flow<'environment,'error,'value> -> Flow<'input,'error,'value>Builds an environment with a layer, runs a downstream flow, and always closes the layer scope. This is the provisioning boundary. It creates a fresh scope, builds the supplied layer inside that scope, runs the downstream flow with the built environment, and finalizes all acquired resources when the downstream flow completes or fails. The layer that builds the downstream environment. The flow to run with the provided environment. A flow that requires only the input environment of the layer. let program () = let runtimeLayer = Layer.succeed "production" let workflow = Flow.envWith String.length Layer.provide runtimeLayer workflow
provision: Layer<unit,ProvisionError,(int * IClock)>map: ('output -> 'next) -> Layer<'input,'error,'output> -> Layer<'input,'error,'next>Maps the successful output of a layer.
ignore: 'T -> unitIgnore the passed value. This is often used to throw away results of a computation. The value to ignore. ignore 55555 // Evaluates to ()
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
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.
FailureThe workflow failed due to a specific cause.
Axial.Cause`1Represents the cause of a failed workflow. The type of the domain-specific failure value.
FailAn expected domain-specific failure.
OrdersProblemMissingOrdersRequires & types
Requires: open Axial; open Axial.Layers (packages: Axial, Axial.Layers)zip, zipPar, And merge
Layer.zip provisions left then right, sequentially. Layer.zipPar (its layer-domain name is Layer.merge)
provisions both sides independently in parallel and returns a tuple. Layer.merge does not synthesize a new
environment type; it only provisions both sides, so build the final environment yourself:
let mergedLayer : Layer<Map<string, string>, StartupError, AppEnv> =
Layer.merge configLayer clockLayer
|> Layer.bind (fun (config, clock) ->
ordersLayerFromConfig config
|> Layer.map (fun orders -> { Config = config; Orders = orders; Clock = clock }))
mergedLayer: Layer<Map<string,string>,StartupError,AppEnv>Axial.Layers.Layer`3Represents a provisioning step that builds an explicit environment inside a scope. The input environment required to build the layer. The typed failure produced during provisioning. The environment or service bundle produced by the layer.
Microsoft.FSharp.Collections.FSharpMap`2Immutable maps based on binary trees, where keys are ordered by F# generic comparison. By default comparison is the F# structural comparison function or uses implementations of the IComparable interface on key values. See the module for further operations on maps. All members of this class are thread-safe and may be used concurrently from multiple threads.
stringAn abbreviation for the CLI type . Basic Types
FsLiveDocsGeneratedPage7_F9357565CFEE.StartupErrorFsLiveDocsGeneratedPage7_F9357565CFEE.AppEnvAxial.Layers.LayerModulemerge: Layer<'input,'error,'left> -> Layer<'input,'error,'right> -> Layer<'input,'error,('left * 'right)>Merges two independent service layers in parallel. merge is the layer-domain name for zipPar. Use it when combining service bundles or environment fragments that do not depend on each other.
configLayer: Layer<Map<string,string>,StartupError,Config>clockLayer: Layer<Map<string,string>,StartupError,IClock>(|>): '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
bind: ('output -> Layer<'input,'error,'next>) -> Layer<'input,'error,'output> -> Layer<'input,'error,'next>Sequences layer provisioning with a dependent follow-up layer.
config: Configclock: IClockordersLayerFromConfig: Config -> Layer<Map<string,string>,StartupError,IOrderRepository>map: ('output -> 'next) -> Layer<'input,'error,'output> -> Layer<'input,'error,'next>Maps the successful output of a layer.
orders: IOrderRepositoryConfig: ConfigOrders: IOrderRepositoryClock: IClockRequires & types
Requires: open Axial.Layers (packages: Axial.Layers)val mergedLayer: Layer<Map<string,string>,StartupError,AppEnv>countOrders
|> Layer.provide mergedLayer
|> Flow.run (Map [ "orders", "db://replica" ])
|> shouldEqual (Exit.Success "3 orders in db://replica")
countOrders: Flow<AppEnv,StartupError,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
Axial.Layers.LayerModuleprovide: Layer<'input,'error,'environment> -> Flow<'environment,'error,'value> -> Flow<'input,'error,'value>Builds an environment with a layer, runs a downstream flow, and always closes the layer scope. This is the provisioning boundary. It creates a fresh scope, builds the supplied layer inside that scope, runs the downstream flow with the built environment, and finalizes all acquired resources when the downstream flow completes or fails. The layer that builds the downstream environment. The flow to run with the provided environment. A flow that requires only the input environment of the layer. let program () = let runtimeLayer = Layer.succeed "production" let workflow = Flow.envWith String.length Layer.provide runtimeLayer workflow
mergedLayer: Layer<Map<string,string>,StartupError,AppEnv>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``: (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")]
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.
Requires & types
Requires: open Axial; open Axial.Layers (packages: Axial, Axial.Layers)Named environment fields make service requirements explicit and avoid ambiguity when two services share an implementation type. If an application needs multiple instances of the same service shape, give them named record fields or distinct nominal contracts rather than relying on tags.
Cleanup
Layer.provide creates a root scope, builds the layer, runs the downstream flow, and closes the scope. Cleanup runs when
the layer fails, the downstream flow fails, or the downstream flow succeeds.
If a later step fails after an earlier one acquired a resource, the resource is released when the root scope closes:
events.Clear()
let halfBuilt : Layer<Map<string, string>, StartupError, IOrderRepository * IClock> =
Layer.zip (ordersLayerFromConfig { ConnectionString = "db://orders" }) (fail (MissingSetting "clock"))
Flow.ok 0
|> Layer.provide (halfBuilt |> Layer.map ignore)
|> Flow.run Map.empty
|> shouldEqual (Exit.Failure(Cause.Fail(MissingSetting "clock")))
List.ofSeq events |> shouldEqual [ "opened db://orders"; "closed db://orders" ]
events: ResizeArray<string>Clear: unit -> unitRemoves all elements from the .
halfBuilt: Layer<Map<string,string>,StartupError,(IOrderRepository * IClock)>Axial.Layers.Layer`3Represents a provisioning step that builds an explicit environment inside a scope. The input environment required to build the layer. The typed failure produced during provisioning. The environment or service bundle produced by the layer.
Microsoft.FSharp.Collections.FSharpMap`2Immutable maps based on binary trees, where keys are ordered by F# generic comparison. By default comparison is the F# structural comparison function or uses implementations of the IComparable interface on key values. See the module for further operations on maps. All members of this class are thread-safe and may be used concurrently from multiple threads.
stringAn abbreviation for the CLI type . Basic Types
FsLiveDocsGeneratedPage7_F9357565CFEE.StartupErrorFsLiveDocsGeneratedPage7_F9357565CFEE.IOrderRepositoryAxial.IClockSupplies wall time, monotonic time, and cancellable delays from one source. Wall time is for timestamps. Use differences between Elapsed readings for durations.
Axial.Layers.LayerModulezip: Layer<'input,'error,'left> -> Layer<'input,'error,'right> -> Layer<'input,'error,('left * 'right)>Builds two layers from the same input and scope and returns both outputs. zip is sequential: the left layer is provisioned before the right layer. Use zipPar or merge for independent parallel provisioning.
ordersLayerFromConfig: Config -> Layer<Map<string,string>,StartupError,IOrderRepository>ConnectionString: stringfail: StartupError -> Layer<'input,StartupError,'output>MissingSettingAxial.Flowok: 'value -> Flow<'env,'error,'value>Creates a successful synchronous flow. The value to wrap in a successful flow. A flow that always succeeds with the provided value.
(|>): '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
provide: Layer<'input,'error,'environment> -> Flow<'environment,'error,'value> -> Flow<'input,'error,'value>Builds an environment with a layer, runs a downstream flow, and always closes the layer scope. This is the provisioning boundary. It creates a fresh scope, builds the supplied layer inside that scope, runs the downstream flow with the built environment, and finalizes all acquired resources when the downstream flow completes or fails. The layer that builds the downstream environment. The flow to run with the provided environment. A flow that requires only the input environment of the layer. let program () = let runtimeLayer = Layer.succeed "production" let workflow = Flow.envWith String.length Layer.provide runtimeLayer workflow
map: ('output -> 'next) -> Layer<'input,'error,'output> -> Layer<'input,'error,'next>Maps the successful output of a layer.
ignore: 'T -> unitIgnore the passed value. This is often used to throw away results of a computation. The value to ignore. ignore 55555 // Evaluates to ()
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
Microsoft.FSharp.Collections.MapModuleContains operations for working with values of type .
empty: Map<'Key,'T>The empty map. This is an O(1) operation. let emptyMap = Map.empty<int, string>
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.
FailureThe workflow failed due to a specific cause.
Axial.Cause`1Represents the cause of a failed workflow. The type of the domain-specific failure value.
FailAn expected domain-specific failure.
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.
ofSeq: 'T seq -> 'T listBuilds a new list from the given enumerable object. The input sequence. The list of elements from the sequence. let inputs = seq { 1; 2; 5 } inputs |> List.ofSeq Evaluates to [ 1; 2; 5 ]. This is an O(n) operation, where n is the length of the sequence.
Requires & types
Requires: open Axial; open Axial.Layers (packages: Axial, Axial.Layers)val halfBuilt: Layer<Map<string,string>,StartupError,(IOrderRepository * IClock)>Parallel composition uses parent-owned child scopes, so the same holds when one parallel branch fails after another
acquired resources. If both parallel branches fail, Axial preserves both failures as Cause.Both (leftCause, rightCause) rather than
discarding one side.
let failOrders : Layer<unit, ProvisionError, int> =
Layer.fromFlow (Flow.fail (OrdersProblem(MissingOrders "orders")))
let failClock : Layer<unit, ProvisionError, IClock> =
Layer.fromFlow (Flow.fail (ClockProblem(BrokenClock "clock")))
failOrders: Layer<unit,ProvisionError,int>Axial.Layers.Layer`3Represents a provisioning step that builds an explicit environment inside a scope. The input environment required to build the layer. The typed failure produced during provisioning. The environment or service bundle produced by the layer.
unitThe type 'unit', which has only one value "()". This value is special and always uses the representation 'null'. Basic Types
FsLiveDocsGeneratedPage7_F9357565CFEE.ProvisionErrorintAn abbreviation for the CLI type . Basic Types
Axial.Layers.LayerModulefromFlow: Flow<'input,'error,'output> -> Layer<'input,'error,'output>Creates a layer that builds its output by running a flow with the layer's input as its environment. The flow runs in the layer's scope, so what it registers with Flow.scopeAcquireRelease, Flow.scopeFinalizer, or Flow.scopeResource stays open until the Layer.provide boundary closes, and is released in reverse acquisition order. A typed failure fails provisioning; whatever was acquired before it is released. The flow that builds the layer output. A layer that succeeds with the flow's value. let database : Layer<AppConfig, DbError, Database> = Layer.fromFlow (Flow.scopeAcquireRelease (Database.connect ()) Database.close)
Axial.Flowfail: '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")
OrdersProblemMissingOrdersfailClock: Layer<unit,ProvisionError,IClock>Axial.IClockSupplies wall time, monotonic time, and cancellable delays from one source. Wall time is for timestamps. Use differences between Elapsed readings for durations.
ClockProblemBrokenClockRequires & types
Requires: open Axial; open Axial.Layers (packages: Axial, Axial.Layers)val failOrders: Layer<unit,ProvisionError,int>val failClock: Layer<unit,ProvisionError,IClock>Flow.ok 0
|> Layer.provide (Layer.merge failOrders failClock |> Layer.map ignore)
|> Flow.run ()
|> function
| Exit.Failure cause ->
Cause.failures cause
|> shouldEqual [ OrdersProblem(MissingOrders "orders"); ClockProblem(BrokenClock "clock") ]
| other -> failwithf "Expected a failure, got %A" other
Axial.Flowok: 'value -> Flow<'env,'error,'value>Creates a successful synchronous flow. The value to wrap in a successful flow. A flow that always succeeds with the provided value.
(|>): '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.Layers.LayerModuleprovide: Layer<'input,'error,'environment> -> Flow<'environment,'error,'value> -> Flow<'input,'error,'value>Builds an environment with a layer, runs a downstream flow, and always closes the layer scope. This is the provisioning boundary. It creates a fresh scope, builds the supplied layer inside that scope, runs the downstream flow with the built environment, and finalizes all acquired resources when the downstream flow completes or fails. The layer that builds the downstream environment. The flow to run with the provided environment. A flow that requires only the input environment of the layer. let program () = let runtimeLayer = Layer.succeed "production" let workflow = Flow.envWith String.length Layer.provide runtimeLayer workflow
merge: Layer<'input,'error,'left> -> Layer<'input,'error,'right> -> Layer<'input,'error,('left * 'right)>Merges two independent service layers in parallel. merge is the layer-domain name for zipPar. Use it when combining service bundles or environment fragments that do not depend on each other.
failOrders: Layer<unit,ProvisionError,int>failClock: Layer<unit,ProvisionError,IClock>map: ('output -> 'next) -> Layer<'input,'error,'output> -> Layer<'input,'error,'next>Maps the successful output of a layer.
ignore: 'T -> unitIgnore the passed value. This is often used to throw away results of a computation. The value to ignore. ignore 55555 // Evaluates to ()
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
Axial.Exit`2Represents the final outcome of a workflow execution. The type of the success value. The type of the domain-specific failure value.
FailureThe workflow failed due to a specific cause.
cause: Cause<ProvisionError>Axial.Causefailures: Cause<'error> -> 'error listReturns every typed failure value contained in a cause tree.
shouldEqual: 'a -> 'a -> unitOrdersProblemMissingOrdersClockProblemBrokenClockother: Exit<int,ProvisionError>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.
Requires & types
Requires: open Axial; open Axial.Layers (packages: Axial, Axial.Layers)
