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.JavaScriptTroubleshooting Types
This page shows the compiler errors that usually mean you crossed a wrapper boundary in the wrong place.
Most Axial type errors are not exotic. The compiler usually sees one wrapper shape and you intended another.
Error: A Flow Alias Does Not Match The Channels You Intended
Flow<'env, 'error, 'value> is the full workflow shape. The shorter aliases remove common channels:
| Alias | Expands to |
|---|---|
Flow<'value> |
Flow<unit, Never, 'value> |
Flow<'error, 'value> |
Flow<unit, 'error, 'value> |
EnvFlow<'env, 'value> |
Flow<'env, Never, 'value> |
ExnFlow<'value> |
Flow<unit, exn, 'value> |
ExnEnvFlow<'env, 'value> |
Flow<'env, exn, 'value> |
If your workflow reads an environment and has a typed domain error, use the full Flow<'env, 'error, 'value> form.
Error: A Unique Overload For Method Bind Could Not Be Determined
This usually happens when the compiler cannot tell which wrapper shape a let! value should use.
Example:
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.
let nested : Async<Async<Result<int, string>>> =
async {
return async { return Ok 42 }
}
let workflow : Flow<unit, string, int> =
flow {
let! next = nested
let! value = next
return value
}
nested: Async<Async<Result<int,string>>>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
Microsoft.FSharp.Core.FSharpResult`2Helper type for error handling without exceptions. Choices and Results
intAn abbreviation for the CLI type . Basic Types
stringAn 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
OkRepresents an OK or a Successful result. The code succeeded with a value of 'T.
workflow: Flow<unit,string,int>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.
next: Async<Result<int,string>>value: intThe second let! is ambiguous.
Fix it with a type annotation:
let annotated : Flow<unit, string, int> =
flow {
let! next = nested
let! (value: int) = next
return value
}
annotated: Flow<unit,string,int>Axial.Flow`3Represents a cold workflow that reads an environment, returns a typed result, and is executed explicitly through one of its execution members such as ToTask, ToAsync, or RunSynchronously. The type of the environment dependency. The type of the failure value. The type of the success value.
unitThe type 'unit', which has only one value "()". This value is special and always uses the representation 'null'. Basic Types
stringAn abbreviation for the CLI type . Basic Types
intAn abbreviation for the CLI type . Basic Types
flow: FlowBuilderThe universal flow { } computation expression.
next: Async<Result<int,string>>nested: Async<Async<Result<int,string>>>value: intannotated |> Flow.run () |> shouldEqual (Exit.Success 42)
annotated: Flow<unit,string,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.
Error: The Flow Requires A Different Environment Type
This usually means you wrote a smaller workflow against one env type (or a specific service contract) and are trying to run it inside a larger env.
Example 1: Records
type SmallEnv = { Prefix: string }
type BigEnv = { App: SmallEnv; RequestId: string }
let greet : Flow<SmallEnv, string, string> =
flow {
let! prefix = Flow.envWith _.Prefix
return $"{prefix} world"
}
// Run in BigEnv using localEnv
let greetInBigEnv : Flow<BigEnv, string, string> =
greet |> Flow.localEnv _.App
FsLiveDocsGeneratedPage9_4556FC416C2B.SmallEnvPrefix: stringstringAn abbreviation for the CLI type . Basic Types
FsLiveDocsGeneratedPage9_4556FC416C2B.BigEnvApp: SmallEnvRequestId: stringgreet: Flow<SmallEnv,string,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.
flow: FlowBuilderThe universal flow { } computation expression.
prefix: stringAxial.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: SmallEnvgreetInBigEnv: Flow<BigEnv,string,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
localEnv: ('outerEnvironment -> 'innerEnvironment) -> Flow<'innerEnvironment,'error,'value> -> Flow<'outerEnvironment,'error,'value>Runs a flow against an environment derived from the outer environment. Use this to embed a smaller workflow inside a larger application environment without changing the smaller workflow's type. The mapping is applied at execution time. This is useful for preserving narrow helper signatures while still running everything from one app boundary. A function that maps the outer environment to the inner environment. The flow to run with the inner environment. A flow that expects the outer environment. let flow = Flow.succeed 1 |> Flow.localEnv (fun outer -> outer)
_arg1: BigEnvgreetInBigEnv
|> Flow.run { App = { Prefix = "hello" }; RequestId = "r-1" }
|> shouldEqual (Exit.Success "hello world")
greetInBigEnv: Flow<BigEnv,string,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
App: SmallEnvPrefix: stringRequestId: stringshouldEqual: '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.
Example 2: Services
If a helper requires IHasDatabase but you are running it in an environment that doesn't implement it, the compiler will error.
let helper : Flow<#IHasDatabase, _, _> = ...
// This fails if AppEnv doesn't implement IHasDatabase
let run (env: AppEnv) = helper |> Flow.startTask envFix it by implementing the interface on your environment type.
Error: Option Or ValueOption Does Not Match Your Error Type
Implicit option binding only works when the workflow error type is unit.
This fails:
let workflow : Flow<unit, string, int> =
flow {
let! value = Some 42
return value
}Use an explicit adapter when you want a custom error:
let optionWorkflow : Flow<unit, string, int> =
Some 42
|> Flow.fromOption "missing value"
optionWorkflow: Flow<unit,string,int>Axial.Flow`3Represents a cold workflow that reads an environment, returns a typed result, and is executed explicitly through one of its execution members such as ToTask, ToAsync, or RunSynchronously. The type of the environment dependency. The type of the failure value. The type of the success value.
unitThe type 'unit', which has only one value "()". This value is special and always uses the representation 'null'. Basic Types
stringAn abbreviation for the CLI type . Basic Types
intAn abbreviation for the CLI type . Basic Types
SomeThe representation of "Value of type 'T" The input value. An option representing the 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.FlowfromOption: 'error -> 'value option -> Flow<'env,'error,'value>Lifts an option into a synchronous flow with the supplied error. The error to return if the option is None. The option to lift. A flow that succeeds with the option's value or fails with the provided error. let opt = Some "value" Flow.fromOption "missing" opt |> Flow.run ()
optionWorkflow |> Flow.run () |> shouldEqual (Exit.Success 42)
(None |> Flow.fromOption "missing value" : Flow<unit, string, int>) |> Flow.run () |> shouldEqual (Exit.Failure(Cause.Fail "missing value"))
optionWorkflow: Flow<unit,string,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.
NoneThe representation of "No value"
fromOption: 'error -> 'value option -> Flow<'env,'error,'value>Lifts an option into a synchronous flow with the supplied error. The error to return if the option is None. The option to lift. A flow that succeeds with the option's value or fails with the provided error. let opt = Some "value" Flow.fromOption "missing" opt |> Flow.run ()
Axial.Flow`3Represents a cold workflow that reads an environment, returns a typed result, and is executed explicitly through one of its execution members such as ToTask, ToAsync, or RunSynchronously. The type of the environment dependency. The type of the failure value. The type of the success value.
unitThe type 'unit', which has only one value "()". This value is special and always uses the representation 'null'. Basic Types
stringAn abbreviation for the CLI type . Basic Types
intAn 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: Task is not a Flow builder source
Raw Task<'value> and ValueTask<'value> values do not bind directly in flow { }. A task is already running, while
Flow is a cold description that may run more than once.
Wrap work that should start when the Flow runs in ColdTask:
let loadAsync (cancellationToken: CancellationToken) : Task<int> =
task {
do! Task.Delay(1, cancellationToken)
return 42
}
let load : ColdTask<int> = ColdTask loadAsync
let coldWorkflow : Flow<int> =
flow {
let! value = load
return value
}
loadAsync: CancellationToken -> Task<int>cancellationToken: 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 .
intAn abbreviation for the CLI type . Basic Types
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.
load: ColdTask<int>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.
ColdTaskcoldWorkflow: Flow<int>FlowA flow that requires no environment and cannot fail with a typed error.
flow: FlowBuilderThe universal flow { } computation expression.
value: intcoldWorkflow |> Flow.run () |> shouldEqual (Exit.Success 42)
coldWorkflow: 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.
If the cold task returns Result<'value,'error>, let! and return! place Error in Flow's typed error channel.
When work has already started, name that lifecycle explicitly:
let runningTask : Task<int> = loadAsync CancellationToken.None
let startedWorkflow : Flow<int> =
flow {
let! value = Flow.awaitStartedTask runningTask
return value
}
runningTask: Task<int>System.Threading.Tasks.Task`1Represents an asynchronous operation that can return a value. The type of the result produced by this .
intAn abbreviation for the CLI type . Basic Types
loadAsync: CancellationToken -> Task<int>System.Threading.CancellationTokenPropagates notification that operations should be canceled.
None: CancellationTokenReturns an empty value. An empty cancellation token.
startedWorkflow: Flow<int>FlowA flow that requires no environment and cannot fail with a typed error.
flow: FlowBuilderThe universal flow { } computation expression.
value: intAxial.FlowawaitStartedTask: Task<'value> -> Flow<'env,'error,'value>Observes a task that has already been started. The operation is in flight before this is called. It therefore starts outside the workflow, ignores the runtime's cancellation token, and yields the same single result no matter how many times the flow is executed. Prefer fromTask, which keeps the flow cold. Thrown exceptions are recorded as defects (Cause.Die). A task that is already running. .NET only
startedWorkflow |> Flow.run () |> shouldEqual (Exit.Success 42)
startedWorkflow: 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.
Use Flow.awaitStartedTaskResult when the started task returns Result.
When Type Errors Usually Mean A Boundary Problem
If the compiler error mentions one of these shapes, check the boundary first:
Result<...>Async<...>Async<Result<...>>Task<...>Task<Result<...>>Flow<...>
Flow.retry and Flow.repeat take a Schedule. If the compiler reports a mismatch on the schedule's input type, check that the schedule's input matches the flow's error type (for retry) or value type (for repeat).
Most fixes are one of:
- add a type annotation to disambiguate
let!overloads - derive a smaller local environment with
localEnv - use
Bind.errororBind.mapErrorat aflow { }bind site when the source error must be assigned or mapped first - move back to plain Result until the real workflow boundary appears

