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.JavaScriptAsync and Task interop
Axial distinguishes a description of asynchronous work from work that has already started. Use that distinction to keep
workflows rerunnable, pass cancellation to external operations, and preserve Result.Error in Flow's typed error
channel.
Choose an interop form
| Source | Use | Behavior |
|---|---|---|
Async<'value> |
Bind directly in flow { } |
Cold and rerunnable; the value is successful output |
Async<Result<'value,'error>> |
Bind directly in flow { } |
Cold and rerunnable; Error enters the typed error channel |
ColdTask<'value> |
Bind directly in flow { } |
Cold task factory; receives Flow's cancellation token |
ColdTask<Result<'value,'error>> |
Bind directly in flow { } |
Cold task factory; Error enters the typed error channel |
CancellationToken -> Task<'value> |
Flow.fromTask |
Creates a cold Flow whose task value is successful output |
CancellationToken -> Task<Result<'value,'error>> |
Flow.fromTaskResult |
Creates a cold Flow whose Error enters the typed error channel |
Already-running Task<'value> |
Flow.awaitStartedTask |
Awaits the existing operation; Flow cannot pass cancellation into it |
Already-running Task<Result<'value,'error>> |
Flow.awaitStartedTaskResult |
Awaits the existing operation and lifts Error |
ValueTask has the corresponding fromValueTask, fromValueTaskResult, awaitStartedValueTask, and
awaitStartedValueTaskResult functions.
The examples on this page
The examples use a stand-in for a data-access class. Its LoadUserAsync method starts work as soon as it is called,
like most .NET APIs that return a Task, and it counts how often it was called:
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.
type User = { Id: int; Name: string }
type LoadUserError = UserNotFound of int
type UserRepository() =
let mutable calls = 0
member _.Calls = calls
member _.LoadUserAsync(id: int, cancellationToken: CancellationToken) : Task<Result<User, LoadUserError>> =
calls <- calls + 1
task {
do! Task.Delay(1, cancellationToken)
return if id = 1 then Ok { Id = 1; Name = "Ada" } else Error(UserNotFound id)
}
member this.LoadUser(id: int) : Async<Result<User, LoadUserError>> =
async {
let! cancellationToken = Async.CancellationToken
return! this.LoadUserAsync(id, cancellationToken) |> Async.AwaitTask
}
FsLiveDocsGeneratedPage8_4556FC416C2B.UserId: intintAn abbreviation for the CLI type . Basic Types
Name: stringstringAn abbreviation for the CLI type . Basic Types
FsLiveDocsGeneratedPage8_4556FC416C2B.LoadUserErrorUserNotFoundFsLiveDocsGeneratedPage8_4556FC416C2B.UserRepositorycalls: int_: UserRepositoryCalls: UserRepository -> unit -> intLoadUserAsync: UserRepository -> int * CancellationToken -> Task<Result<User,LoadUserError>>id: intcancellationToken: CancellationTokenSystem.Threading.CancellationTokenPropagates notification that operations should be canceled.
System.Threading.Tasks.Task`1Represents an asynchronous operation that can return a value. The type of the result produced by this .
Microsoft.FSharp.Core.FSharpResult`2Helper type for error handling without exceptions. Choices and Results
(+): ^T1 -> ^T2 -> ^T3Overloaded addition operator The first parameter. The second parameter. The result of the operation. 2 + 2 // Evaluates to 4 "Hello " + "World" // Evaluates to "Hello World"
task: TaskBuilderBuilds a task using computation expression syntax.
System.Threading.Tasks.TaskRepresents an asynchronous operation.
Delay: int * CancellationToken -> TaskCreates a cancellable task that completes after a specified number of milliseconds. The number of milliseconds to wait before completing the returned task, or -1 to wait indefinitely. A cancellation token to observe while waiting for the task to complete. A task that represents the time delay. The argument is less than -1. The task has been canceled. The provided has already been disposed.
(=): '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.
ErrorRepresents an Error or a Failure. The code failed with a value of 'TError representing what went wrong.
this: UserRepositoryLoadUser: UserRepository -> int -> Async<Result<User,LoadUserError>>Microsoft.FSharp.Control.FSharpAsync`1An asynchronous computation, which, when run, will eventually produce a value of type T, or else raises an exception. This type has no members. Asynchronous computations are normally specified either by using an async expression or the static methods in the type. See also F# Language Guide - Async Workflows. Library functionality for asynchronous programming, events and agents. See also Asynchronous Programming, Events and Lazy Expressions in the F# Language Guide. Async Programming
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
Microsoft.FSharp.Control.FSharpAsyncHolds static members for creating and manipulating asynchronous computations. See also F# Language Guide - Async Workflows. Async Programming
CancellationToken: Async<CancellationToken>Creates an asynchronous computation that returns the CancellationToken governing the execution of the computation. In async { let! token = Async.CancellationToken ...} token can be used to initiate other asynchronous operations that will cancel cooperatively with this workflow. An asynchronous computation capable of retrieving the CancellationToken from a computation expression. Cancellation and Exceptions
LoadUserAsync: int * CancellationToken -> Task<Result<User,LoadUserError>>(|>): '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
AwaitTask: Task<'T> -> Async<'T>Return an asynchronous computation that will wait for the given task to complete and return its result. The task to await. If an exception occurs in the asynchronous computation then an exception is re-raised by this function. If the task is cancelled then is raised. Note that the task may be governed by a different cancellation token to the overall async computation where the AwaitTask occurs. In practice you should normally start the task with the cancellation token returned by let! ct = Async.CancellationToken, and catch any at the point where the overall async is started. Awaiting Results
Bind Async values
Async is already a cold F# computation. Bind it directly:
let loadCount : Async<int> = async { return 42 }
let plusOne : Flow<int> =
flow {
let! count = loadCount
return count + 1
}
loadCount: Async<int>Microsoft.FSharp.Control.FSharpAsync`1An asynchronous computation, which, when run, will eventually produce a value of type T, or else raises an exception. This type has no members. Asynchronous computations are normally specified either by using an async expression or the static methods in the type. See also F# Language Guide - Async Workflows. Library functionality for asynchronous programming, events and agents. See also Asynchronous Programming, Events and Lazy Expressions in the F# Language Guide. Async Programming
intAn abbreviation for the CLI type . Basic Types
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
plusOne: Flow<int>FlowA flow that requires no environment and cannot fail with a typed error.
flow: FlowBuilderThe universal flow { } computation expression.
count: int(+): ^T1 -> ^T2 -> ^T3Overloaded addition operator The first parameter. The second parameter. The result of the operation. 2 + 2 // Evaluates to 4 "Hello " + "World" // Evaluates to "Hello World"
An outer Result is part of the Flow contract, not a nested success value: Ok continues the block and Error enters
the typed error channel. The same applies to return!:
let repository = UserRepository()
let userName (id: int) : Flow<LoadUserError, string> =
flow {
let! user = repository.LoadUser id
return user.Name
}
let loadUser (id: int) : Flow<LoadUserError, User> = flow { return! repository.LoadUser id }
repository: UserRepository``.ctor``: unit -> UserRepositoryuserName: int -> Flow<LoadUserError,string>id: intintAn abbreviation for the CLI type . Basic Types
FlowA flow that requires no environment and can fail with a typed error.
FsLiveDocsGeneratedPage8_4556FC416C2B.LoadUserErrorstringAn abbreviation for the CLI type . Basic Types
flow: FlowBuilderThe universal flow { } computation expression.
user: UserLoadUser: int -> Async<Result<User,LoadUserError>>Name: stringloadUser: int -> Flow<LoadUserError,User>FsLiveDocsGeneratedPage8_4556FC416C2B.UserplusOne |> Flow.run () |> shouldEqual (Exit.Success 43)
userName 1 |> Flow.run () |> shouldEqual (Exit.Success "Ada")
userName 2 |> Flow.run () |> shouldEqual (Exit.Failure(Cause.Fail(UserNotFound 2)))
loadUser 1 |> Flow.run () |> shouldEqual (Exit.Success { Id = 1; Name = "Ada" })
plusOne: Flow<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
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
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.
userName: int -> Flow<LoadUserError,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.
UserNotFoundloadUser: int -> Flow<LoadUserError,User>Id: intName: stringUse Flow.fromAsync or Flow.fromAsyncResult when composing without flow { }.
Bind cold Task work
A Task starts when the method that returns it runs. Wrap the call in ColdTask so the method runs only when the Flow
runs. ColdTask<Result<_,_>> lifts its outer Result for both let! and return!:
let loadUserCold (id: int) : ColdTask<Result<User, LoadUserError>> =
ColdTask(fun cancellationToken -> repository.LoadUserAsync(id, cancellationToken))
let coldName (id: int) : Flow<LoadUserError, string> =
flow {
let! user = loadUserCold id
return user.Name
}
loadUserCold: int -> ColdTask<Result<User,LoadUserError>>id: intintAn abbreviation for the CLI type . Basic Types
Axial.ColdTask`1Represents delayed task work that can observe a runtime cancellation token when it is started. Bind a cold task directly in flow { }. When the task produces Result<'value,'error>, the builder places Error in Flow's typed error channel for both let! and return!. A raw, already-started Task is not a Flow builder source. The type of the produced task value.
Microsoft.FSharp.Core.FSharpResult`2Helper type for error handling without exceptions. Choices and Results
FsLiveDocsGeneratedPage8_4556FC416C2B.UserFsLiveDocsGeneratedPage8_4556FC416C2B.LoadUserErrorColdTaskcancellationToken: CancellationTokenrepository: UserRepositoryLoadUserAsync: int * CancellationToken -> Task<Result<User,LoadUserError>>coldName: int -> Flow<LoadUserError,string>FlowA flow that requires no environment and can fail with a typed error.
stringAn abbreviation for the CLI type . Basic Types
flow: FlowBuilderThe universal flow { } computation expression.
user: UserName: stringBuilding the flow calls nothing. Each execution calls the method again and passes that execution's cancellation token, so retry, repeat, timeout, race, and interruption operate on newly started work:
let before = repository.Calls
let described = coldName 1
repository.Calls |> shouldEqual before
described |> Flow.run () |> shouldEqual (Exit.Success "Ada")
described |> Flow.run () |> shouldEqual (Exit.Success "Ada")
repository.Calls |> shouldEqual (before + 2)
before: intrepository: UserRepositoryCalls: intdescribed: Flow<LoadUserError,string>coldName: int -> Flow<LoadUserError,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
shouldEqual: 'a -> 'a -> unitAxial.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
Axial.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.
(+): ^T1 -> ^T2 -> ^T3Overloaded addition operator The first parameter. The second parameter. The result of the operation. 2 + 2 // Evaluates to 4 "Hello " + "World" // Evaluates to "Hello World"
Convert a Task factory without a builder
Flow.fromTask takes a function from the cancellation token to a task that returns an ordinary value.
Flow.fromTaskResult takes one whose task returns a Result, and sends Error to the typed error channel:
let measure (text: string) : Flow<int> =
Flow.fromTask (fun cancellationToken ->
task {
do! Task.Delay(1, cancellationToken)
return text.Length
})
let loadFirstUser : Flow<LoadUserError, User> =
Flow.fromTaskResult (fun cancellationToken -> repository.LoadUserAsync(1, cancellationToken))
measure: string -> Flow<int>text: stringstringAn abbreviation for the CLI type . Basic Types
FlowA flow that requires no environment and cannot fail with a typed error.
intAn abbreviation for the CLI type . Basic Types
Axial.FlowfromTask: (CancellationToken -> Task<'value>) -> Flow<'env,'error,'value>Creates a flow from a cancellable task factory. The factory runs on each execution and receives the runtime's cancellation token, so the flow stays cold and cancellable. Thrown exceptions are recorded as defects (Cause.Die). Use attemptTask when expected exceptions should enter the typed error channel. Starts the operation, observing the supplied cancellation token. .NET only let flow = Flow.fromTask (fun token -> client.GetStringAsync(url, token))
cancellationToken: CancellationTokentask: TaskBuilderBuilds a task using computation expression syntax.
System.Threading.Tasks.TaskRepresents an asynchronous operation.
Delay: int * CancellationToken -> TaskCreates a cancellable task that completes after a specified number of milliseconds. The number of milliseconds to wait before completing the returned task, or -1 to wait indefinitely. A cancellation token to observe while waiting for the task to complete. A task that represents the time delay. The argument is less than -1. The task has been canceled. The provided has already been disposed.
Length: intGets the number of characters in the current object. The number of characters in the current string.
loadFirstUser: Flow<LoadUserError,User>FlowA flow that requires no environment and can fail with a typed error.
FsLiveDocsGeneratedPage8_4556FC416C2B.LoadUserErrorFsLiveDocsGeneratedPage8_4556FC416C2B.UserfromTaskResult: (CancellationToken -> Task<Result<'value,'error>>) -> Flow<'env,'error,'value>Creates a flow from a cold task factory whose Error enters the typed error channel. The factory runs on each execution and receives the runtime cancellation token. Thrown exceptions are defects. .NET only
repository: UserRepositoryLoadUserAsync: int * CancellationToken -> Task<Result<User,LoadUserError>>measure "four" |> Flow.run () |> shouldEqual (Exit.Success 4)
loadFirstUser |> Flow.run () |> shouldEqual (Exit.Success { Id = 1; Name = "Ada" })
measure: string -> Flow<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
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
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.
loadFirstUser: Flow<LoadUserError,User>Id: intName: stringBoth functions call their factory on every execution. Thrown exceptions are defects; cancellation is interruption.
Await work that already started
Sometimes an API gives you a Task that is already running. Flow.awaitStartedTask awaits it, and
Flow.awaitStartedTaskResult also lifts an Error:
let callsBefore = repository.Calls
let runningTask = repository.LoadUserAsync(1, CancellationToken.None)
let refresh : Flow<LoadUserError, User> = Flow.awaitStartedTaskResult runningTask
refresh |> Flow.run () |> shouldEqual (Exit.Success { Id = 1; Name = "Ada" })
refresh |> Flow.run () |> shouldEqual (Exit.Success { Id = 1; Name = "Ada" })
repository.Calls |> shouldEqual (callsBefore + 1)
callsBefore: intrepository: UserRepositoryCalls: intrunningTask: Task<Result<User,LoadUserError>>LoadUserAsync: int * CancellationToken -> Task<Result<User,LoadUserError>>System.Threading.CancellationTokenPropagates notification that operations should be canceled.
None: CancellationTokenReturns an empty value. An empty cancellation token.
refresh: Flow<LoadUserError,User>FlowA flow that requires no environment and can fail with a typed error.
FsLiveDocsGeneratedPage8_4556FC416C2B.LoadUserErrorFsLiveDocsGeneratedPage8_4556FC416C2B.UserAxial.FlowawaitStartedTaskResult: Task<Result<'value,'error>> -> Flow<'env,'error,'value>Observes an already-started task whose Error enters the typed error channel. The work started outside Flow and cannot receive the runtime cancellation token. Thrown exceptions are defects. .NET only
(|>): '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
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.
SuccessThe workflow completed successfully.
Id: intName: string(+): ^T1 -> ^T2 -> ^T3Overloaded addition operator The first parameter. The second parameter. The result of the operation. 2 + 2 // Evaluates to 4 "Hello " + "World" // Evaluates to "Hello World"
The repository was called once, when runningTask was created. An already-running task has different lifecycle
semantics:
- It started before the Flow.
- Reusing the Flow awaits the same operation.
- Flow cannot pass its cancellation token into work that already started.
- Prefer a cold factory when you control task creation.
Raw Task and ValueTask values do not bind directly in flow { }. This prevents an already-running operation from
looking like a cold workflow description.
Handle expected exceptions
The from*, ColdTask, and awaitStarted* paths treat thrown exceptions as defects. Use an attempt* function when
an exception is an expected failure that callers should handle:
let readFile (path: string) : ExnFlow<string> =
Flow.attemptTask (fun cancellationToken -> File.ReadAllTextAsync(path, cancellationToken))
let failureName (exit: Exit<'value, exn>) =
match exit with
| Exit.Failure(Cause.Fail error) -> error.GetType().Name
| _ -> "no typed failure"
readFile: string -> ExnFlow<string>path: stringstringAn abbreviation for the CLI type . Basic Types
ExnFlowA flow that requires no environment and uses exceptions as recoverable typed errors.
Axial.FlowattemptTask: (CancellationToken -> Task<'value>) -> Flow<'env,exn,'value>Creates a flow from a cancellable task factory and treats thrown exceptions as recoverable typed errors. Successful completion returns Exit.Success. OperationCanceledException returns Cause.Interrupt when the runtime token requested it. Other exceptions, including cancellation the operation raised for its own reasons, return Cause.Fail exn. Starts the operation, observing the supplied cancellation token. .NET only
cancellationToken: CancellationTokenSystem.IO.FileProvides static methods for the creation, copying, deletion, moving, and opening of a single file, and aids in the creation of objects.
ReadAllTextAsync: string * CancellationToken -> Task<string>Asynchronously opens a text file, reads all the text in the file, and then closes the file. The file to open for reading. The token to monitor for cancellation requests. The default value is . A task that represents the asynchronous read operation, which wraps the string containing all text in the file.
failureName: Exit<'value,exn> -> stringexit: Exit<'value,exn>Axial.Exit`2Represents the final outcome of a workflow execution. The type of the success value. The type of the domain-specific failure value.
valueexnAn abbreviation for the CLI type . Basic Types
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.
error: exnGetType: unit -> TypeGets the runtime type of the current instance. A object that represents the exact runtime type of the current instance.
Name: stringGets the name of the current member. A containing the name of this member.
readFile "/no/such/file.txt" |> Flow.run () |> failureName |> shouldEqual "DirectoryNotFoundException"
readFile: string -> ExnFlow<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.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
failureName: Exit<'value,exn> -> stringshouldEqual: 'a -> 'a -> unitAvailable functions include:
Flow.attemptAsync
Flow.attemptTask
Flow.attemptValueTask
Flow.attemptStartedTask
Flow.attemptStartedValueTask
Axial.FlowattemptAsync: Async<'value> -> Flow<'env,exn,'value>Creates a flow from an async operation and treats thrown exceptions as recoverable typed errors. Successful completion returns Exit.Success. OperationCanceledException returns Cause.Interrupt when the runtime token requested it. Other exceptions, including cancellation the operation raised for its own reasons, return Cause.Fail exn. Fable compatible
attemptTask: (CancellationToken -> Task<'value>) -> Flow<'env,exn,'value>Creates a flow from a cancellable task factory and treats thrown exceptions as recoverable typed errors. Successful completion returns Exit.Success. OperationCanceledException returns Cause.Interrupt when the runtime token requested it. Other exceptions, including cancellation the operation raised for its own reasons, return Cause.Fail exn. Starts the operation, observing the supplied cancellation token. .NET only
attemptValueTask: (CancellationToken -> ValueTask<'value>) -> Flow<'env,exn,'value>Creates a flow from a cancellable value-task factory and treats thrown exceptions as recoverable typed errors. Successful completion returns Exit.Success. OperationCanceledException returns Cause.Interrupt when the runtime token requested it. Other exceptions, including cancellation the operation raised for its own reasons, return Cause.Fail exn. Starts the operation, observing the supplied cancellation token. .NET only
attemptStartedTask: Task<'value> -> Flow<'env,exn,'value>Observes a task that has already been started and treats thrown exceptions as recoverable typed errors. Carries the same caveats as awaitStartedTask. A task that is already running. .NET only
attemptStartedValueTask: ValueTask<'value> -> Flow<'env,exn,'value>Observes a value task that has already been started and treats thrown exceptions as recoverable typed errors. Carries the same caveats as awaitStartedTask. A value task that is already running. .NET only
OperationCanceledException and TaskCanceledException become interruption when the runtime's cancellation token
requested them. Cancellation that the operation raised for its own reasons, such as a library's internal timeout, is a
failure like any other exception: Cause.Fail exn from attempt*, a defect from from*.
Wrap blocking calls
Some libraries only offer synchronous, blocking calls: database drivers, LibGit2Sharp, image codecs. Wrap them with
Flow.fromBlocking so the call runs on the thread pool instead of stalling the workflow's thread:
let slowLookup (key: string) : Flow<string> =
Flow.fromBlocking (fun _ ->
Thread.Sleep 5 // stands in for a synchronous driver call
key.ToUpperInvariant())
slowLookup: string -> Flow<string>key: stringstringAn abbreviation for the CLI type . Basic Types
FlowA flow that requires no environment and cannot fail with a typed error.
Axial.FlowfromBlocking: (CancellationToken -> 'value) -> Flow<'env,'error,'value>Creates a flow from synchronous, blocking work, such as a database driver or native library call. On .NET the operation runs on the thread pool, so a blocking call does not stall the thread that runs the workflow. Once started it runs to completion even if the workflow is interrupted, because blocking work cannot be abandoned safely; it receives the runtime's token so it can stop early. Thrown exceptions are defects (Cause.Die); cancellation the token requested is an interruption. On JavaScript the operation runs inline. The blocking operation, observing the supplied cancellation token where it can. Fable compatible let commits = Flow.fromBlocking (fun _ -> repository.Commits |> Seq.truncate 50 |> List.ofSeq)
System.Threading.ThreadCreates and controls a thread, sets its priority, and gets its status.
Sleep: int -> unitSuspends the current thread for the specified number of milliseconds. The number of milliseconds for which the thread is suspended. If the value of the argument is zero, the thread relinquishes the remainder of its time slice to any thread of equal priority that is ready to run. If there are no other threads of equal priority that are ready to run, execution of the current thread is not suspended. The time-out value is negative and is not equal to .
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.
slowLookup "axial" |> Flow.run () |> shouldEqual (Exit.Success "AXIAL")
slowLookup: string -> Flow<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.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
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.
Once started, blocking work runs to completion even if the workflow is interrupted, because it cannot be abandoned
safely. The operation receives the runtime's cancellation token, so a call that can observe it stops early.
Flow.fromBlockingResult sends an Error to the typed error channel, and Flow.attemptBlocking treats thrown
exceptions as Cause.Fail exn. On JavaScript the operation runs inline.
Keep a Result as the successful value
The builder interprets one outer Result as Flow's error channel. Add another successful layer when a nested Result is
the value you intentionally need:
let inspect (id: int) : ColdTask<Result<Result<User, LoadUserError>, Never>> =
ColdTask(fun cancellationToken ->
task {
let! result = repository.LoadUserAsync(id, cancellationToken)
return (Ok result: Result<_, Never>)
})
let lookup (id: int) : Flow<Result<User, LoadUserError>> = flow { return! inspect id }
inspect: int -> ColdTask<Result<Result<User,LoadUserError>,Never>>id: intintAn abbreviation for the CLI type . Basic Types
Axial.ColdTask`1Represents delayed task work that can observe a runtime cancellation token when it is started. Bind a cold task directly in flow { }. When the task produces Result<'value,'error>, the builder places Error in Flow's typed error channel for both let! and return!. A raw, already-started Task is not a Flow builder source. The type of the produced task value.
Microsoft.FSharp.Core.FSharpResult`2Helper type for error handling without exceptions. Choices and Results
FsLiveDocsGeneratedPage8_4556FC416C2B.UserFsLiveDocsGeneratedPage8_4556FC416C2B.LoadUserErrorAxial.NeverRepresents an error channel that cannot occur.
ColdTaskcancellationToken: CancellationTokentask: TaskBuilderBuilds a task using computation expression syntax.
result: Result<User,LoadUserError>repository: UserRepositoryLoadUserAsync: int * CancellationToken -> Task<Result<User,LoadUserError>>OkRepresents an OK or a Successful result. The code succeeded with a value of 'T.
lookup: int -> Flow<Result<User,LoadUserError>>FlowA flow that requires no environment and cannot fail with a typed error.
flow: FlowBuilderThe universal flow { } computation expression.
lookup 2 |> Flow.run () |> shouldEqual (Exit.Success(Error(UserNotFound 2)))
lookup: int -> Flow<Result<User,LoadUserError>>(|>): '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
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.
ErrorRepresents an Error or a Failure. The code failed with a value of 'TError representing what went wrong.
UserNotFoundThe builder lifts the outer Result<_,Never> and leaves the inner Result<User,LoadUserError> as the successful value.
Summary
- Bind
AsyncandColdTaskdirectly inflow { }. - An outer
Result.Erroralways enters Flow's typed error channel. - Use
ColdTaskorFlow.fromTask*to start task work when the Flow runs. - Use
Flow.awaitStarted*only for work that has already started. - Raw
TaskandValueTaskvalues are not Flow builder sources. - Use
attempt*when exceptions are expected failures rather than defects. - Use
Flow.fromBlocking*for synchronous library calls that block.

