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.JavaScriptDependencies
Pass dependencies as ordinary function arguments until several workflows need the same ones and threading them through unrelated callers becomes noise. Then use an environment.
A workflow states what it needs in its environment channel. Build a record and pass it when the workflow runs:
Show page 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 actualtype User = { Id: int; Name: string }
type IUserStore =
abstract Load: int -> Result<User, string>
type AppEnv = { Users: IUserStore }
let loadUser (id: int) : Flow<AppEnv, string, User> =
flow {
let! users = Flow.envWith _.Users
return! users.Load id
}
FsLiveDocsGeneratedPage23_DBB10DAB81D5.UserId: intintAn abbreviation for the CLI type . Basic Types
Name: stringstringAn abbreviation for the CLI type . Basic Types
FsLiveDocsGeneratedPage23_DBB10DAB81D5.IUserStoreLoad: IUserStore -> int -> Result<User,string>Microsoft.FSharp.Core.FSharpResult`2Helper type for error handling without exceptions. Choices and Results
FsLiveDocsGeneratedPage23_DBB10DAB81D5.AppEnvUsers: IUserStoreloadUser: int -> Flow<AppEnv,string,User>id: intAxial.Flow`3flow: FlowBuilderThe universal flow { } computation expression.
users: IUserStoreAxial.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())
_arg1: AppEnvLoad: int -> Result<User,string>let users =
{ new IUserStore with
member _.Load id = if id = 1 then Ok { Id = 1; Name = "Ada" } else Error $"no user {id}" }
loadUser 1 |> Flow.run { Users = users } |> shouldEqual (Exit.Success { Id = 1; Name = "Ada" })
users: IUserStoreFsLiveDocsGeneratedPage23_DBB10DAB81D5.IUserStore_: IUserStoreLoad: int -> Result<User,string>id: int(=): 'T -> 'T -> boolStructural equality The first parameter. The second parameter. The result of the comparison. 5 = 5 // Evaluates to true 5 = 6 // Evaluates to false [1; 2] = [1; 2] // Evaluates to true (1, 5) = (1, 6) // Evaluates to false
OkRepresents an OK or a Successful result. The code succeeded with a value of 'T.
Id: intName: stringErrorRepresents an Error or a Failure. The code failed with a value of 'TError representing what went wrong.
loadUser: int -> Flow<AppEnv,string,User>(|>): '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.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
Users: IUserStoreshouldEqual: '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.
There is no container or registration step. Tests supply a record with fakes in place of live services.
Two further mechanisms build on it. Contracts let a package ask for a service without
knowing your record type; that is how Console.writeLine and the rest of the
built-in services work, and how you would publish your own. Layers are for provisioning
that is itself effectful; see layers, a separate package.
In this section
- The environment: what
'envactually is, and the functions that read it. - Choosing an approach: arguments, records, contracts, and layers compared.
- Service contracts: how a package asks for a dependency it cannot name.
- Providing the environment: building the value at a host boundary.
- Tutorials: the same material worked end to end.
For the services Axial already implements (the clock, console, file system, processes, and HTTP), see built-in services.

