Repository F# setup
open System
open System.IO
open System.Threading
open System.Threading.Tasks
open Axial
open Axial.Layers
open Axial.Console
open Axial.FileSystem
open Axial.Hosting
open Axial.Hosting.Browser
open Axial.Hosting.Node
open Axial.HttpClient
open Axial.PlatformService
open Axial.Process
open Axial.State
open Axial.Telemetry
open Axial.Telemetry.JavaScriptTutorial: Runtime Operations
This tutorial focuses on the operational helpers that sit around a workflow: timeout, retry, cancellation, annotations, and exception translation.
Use these helpers at the application boundary. They are not substitutes for domain rules.
A Small Workflow To Wrap
open System
open System.Threading
type CheckoutError =
| GatewayUnavailable
| CheckoutTimedOut
| CheckoutCancelled
| ReceiptStoreFailed
| UnexpectedGatewayFailure of string
let authorizeCard : Flow<unit, CheckoutError, string> =
flow {
do! Flow.Runtime.sleep (TimeSpan.FromMilliseconds 50)
return "receipt-123"
}
let storeReceipt (receiptId: string) : Flow<unit, CheckoutError, unit> =
flow {
do! Flow.Runtime.sleep (TimeSpan.FromMilliseconds 20)
return ()
}
let notifyCustomer (receiptId: string) : Flow<unit, CheckoutError, unit> =
flow {
do! Flow.Runtime.sleep (TimeSpan.FromMilliseconds 20)
return ()
}
let checkout : Flow<unit, CheckoutError, string> =
flow {
let! receiptId = authorizeCard
do! storeReceipt receiptId
do! notifyCustomer receiptId
return receiptId
}
SystemThreading12-platforms-and-hosting_03-runtime-operations.md_page.CheckoutErrorGatewayUnavailableCheckoutTimedOutCheckoutCancelledReceiptStoreFailedUnexpectedGatewayFailurestringAn abbreviation for the CLI type . Basic Types
authorizeCard: Flow<unit,CheckoutError,string>Axial.Flow`3Represents a cold workflow that reads an environment, returns a typed result, and is executed explicitly through one of its execution members such as ToTask, ToAsync, or RunSynchronously. The type of the environment dependency. The type of the failure value. The type of the success value.
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.
Axial.Flowsleep: TimeSpan -> Flow<'env,'error,unit>Suspends the flow for the specified duration, observing cancellation. The duration to sleep. A flow that completes after the specified delay.
Axial.Flow.RuntimeRuntime helpers for execution-time concerns like cancellation, scope, timeout, retry, and cleanup.
System.TimeSpanRepresents a time interval.
FromMilliseconds: float -> TimeSpanReturns a that represents a specified number of milliseconds. A number of milliseconds. An object that represents . is less than or greater than . -or- is . -or- is . is equal to .
storeReceipt: string -> Flow<unit,CheckoutError,unit>receiptId: stringnotifyCustomer: string -> Flow<unit,CheckoutError,unit>checkout: Flow<unit,CheckoutError,string>Timeout
let checkoutWithTimeout =
checkout
|> Flow.Runtime.timeoutToError (TimeSpan.FromMilliseconds 10) CheckoutTimedOut
checkoutWithTimeout: Flow<unit,CheckoutError,string>checkout: Flow<unit,CheckoutError,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.FlowtimeoutToError: TimeSpan -> 'error -> Flow<'env,'error,'value> -> Flow<'env,'error,'value>Alias for timeout that emphasizes typed failure on timeout.
Axial.Flow.RuntimeRuntime helpers for execution-time concerns like cancellation, scope, timeout, retry, and cleanup.
System.TimeSpanRepresents a time interval.
FromMilliseconds: float -> TimeSpanReturns a that represents a specified number of milliseconds. A number of milliseconds. An object that represents . is less than or greater than . -or- is . -or- is . is equal to .
CheckoutTimedOutRetry
let retryingCheckout =
checkout
|> Flow.Runtime.retry (function
| GatewayUnavailable -> Some (TimeSpan.FromMilliseconds 100)
| ReceiptStoreFailed -> Some (TimeSpan.FromMilliseconds 50)
| _ -> None)Exceptions
let rawGatewayCall : Flow<unit, CheckoutError, string> =
flow {
if DateTime.UtcNow.Second % 2 = 0 then
return raise (InvalidOperationException "gateway client exploded")
return "receipt-123"
}
let safeGatewayCall =
rawGatewayCall
|> Flow.catch (fun ex -> UnexpectedGatewayFailure ex.Message)
rawGatewayCall: Flow<unit,CheckoutError,string>Axial.Flow`3Represents a cold workflow that reads an environment, returns a typed result, and is executed explicitly through one of its execution members such as ToTask, ToAsync, or RunSynchronously. The type of the environment dependency. The type of the failure value. The type of the success value.
unitThe type 'unit', which has only one value "()". This value is special and always uses the representation 'null'. Basic Types
12-platforms-and-hosting_03-runtime-operations.md_page.CheckoutErrorstringAn abbreviation for the CLI type . Basic Types
flow: FlowBuilderThe universal flow { } computation expression.
System.DateTimeRepresents an instant in time, typically expressed as a date and time of day.
UtcNow: DateTimeGets a object that is set to the current date and time on this computer, expressed as the Coordinated Universal Time (UTC). An object whose value is the current UTC date and time.
Second: intGets the seconds component of the date represented by this instance. The seconds component, expressed as a value between 0 and 59.
(%): ^T1 -> ^T2 -> ^T3Overloaded modulo operator The first parameter. The second parameter. The result of the operation. 29 % 5 // Evaluates to 4
(=): '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
raise: Exception -> 'TRaises an exception The exception to raise. The result value. open System.IO exception FileNotFoundException of string let readFile (fileName: string) = if not (File.Exists(fileName)) then raise(FileNotFoundException(fileName)) File.ReadAllText(fileName) readFile "/this-file-doest-exist" When executed, raises a FileNotFoundException.
``.ctor``: string -> unitInitializes a new instance of the class with a specified error message. The message that describes the error.
safeGatewayCall: Flow<unit,CheckoutError,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.Flowcatch: (exn -> 'error) -> Flow<'env,'error,'value> -> Flow<'env,'error,'value>Catches exceptions raised during execution and simple defect outcomes, then maps them to a typed error. Thrown exceptions and simple Cause.Die outcomes are converted to Cause.Fail. Existing typed failures and interruptions are preserved. Compound causes are preserved unchanged. A function of type exn -> 'error to map the exception. The source flow of type to monitor. A that converts recoverable exceptions into typed errors. let flow = Flow.die (System.Exception("boom")) |> Flow.catch (fun ex -> "caught: " + ex.Message)
ex: exnUnexpectedGatewayFailureMessage: stringGets a message that describes the current exception. The error message that explains the reason for the exception, or an empty string ("").
Cancellation
let runCancellable (cancellationToken: CancellationToken) =
task {
let! exit = checkoutWithTimeout.StartAsTask((), cancellationToken = cancellationToken)
match exit with
| Exit.Success receipt -> printfn "Receipt %s" receipt
| Exit.Failure Cause.Interrupt -> printfn "Cancelled"
| Exit.Failure cause -> printfn "%s" (Cause.prettyPrint string cause)
}
runCancellable: CancellationToken -> Task<unit>cancellationToken: CancellationTokenSystem.Threading.CancellationTokenPropagates notification that operations should be canceled.
task: TaskBuilderBuilds a task using computation expression syntax.
exit: Exit<string,CheckoutError>checkoutWithTimeout: Flow<unit,CheckoutError,string>StartAsTask: unit * CancellationToken option -> Task<Exit<string,CheckoutError>>Starts the workflow immediately and returns a task handle for its final exit. The work is already in flight when this returns. Use ToAsync for a cold handle. The environment used by the workflow. The optional cancellation token. Defaults to . A task that completes with the workflow exit. .NET only
cancellationTokenAxial.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.
receipt: stringprintfn: Printf.TextWriterFormat<'T> -> 'TPrint to stdout using the given format, and add a newline. The formatter. The formatted result. See Printf.printfn (link: ) for examples.
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.
InterruptAn administrative signal to stop the workflow (e.g., cancellation).
cause: Cause<CheckoutError>Axial.CauseprettyPrint: ('error -> string) -> Cause<'error> -> stringPretty prints a cause tree for diagnostics.
string: 'T -> stringConverts the argument to a string using ToString. For standard integer and floating point values and any type that implements IFormattable, ToString conversion uses CultureInfo.InvariantCulture. The input value. The converted string. string 'A' // evaluates to "A" string 0xff // evaluates to "255" string -10 // evaluates to "-10"
Annotations
let annotatedCharge =
flow {
let! annotations = Flow.Runtime.annotations
let! traceId = Flow.Runtime.traceId
return annotations, traceId
}Pulling It Together
let guardedCheckout =
safeGatewayCall
|> Flow.bind (fun receiptId ->
flow {
do! storeReceipt receiptId
do! notifyCustomer receiptId
return receiptId
})
|> Flow.Runtime.timeoutToError (TimeSpan.FromSeconds 2) CheckoutTimedOut
|> Flow.Runtime.retry (function
| GatewayUnavailable -> Some (TimeSpan.FromMilliseconds 200)
| ReceiptStoreFailed -> Some (TimeSpan.FromMilliseconds 100)
| _ -> None)- domain validation decides whether the operation should happen at all
- runtime helpers decide how the host should run that operation
Flow.catchdecides which technical exceptions should become typed failures

