FsFlow 0.6.0

Prefix Reserved
dotnet add package FsFlow --version 0.6.0
                    
NuGet\Install-Package FsFlow -Version 0.6.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FsFlow" Version="0.6.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FsFlow" Version="0.6.0" />
                    
Directory.Packages.props
<PackageReference Include="FsFlow" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add FsFlow --version 0.6.0
                    
#r "nuget: FsFlow, 0.6.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package FsFlow@0.6.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=FsFlow&version=0.6.0
                    
Install as a Cake Addin
#tool nuget:?package=FsFlow&version=0.6.0
                    
Install as a Cake Tool

FsFlow

API Still stabilising - wait for 1.0 to avoid breaking changes

<picture> <source media="(prefers-color-scheme: dark)" srcset="docs/content/img/fsflow-readme-dark.svg"> <source media="(prefers-color-scheme: light)" srcset="docs/content/img/fsflow-readme-light.svg"> <img alt="FsFlow" src="docs/content/img/fsflow-readme-light.svg" width="160"> </picture>

FsFlow provides structured composition over normal F#/.NET code. It is a coherent application architecture model for F# on .NET, centered on a unified effect system.

Write small predicate checks with Check, keep fail-fast logic in standard Result, accumulate sibling validation with Validation and validate {}, then lift the same logic into Flow when the boundary needs environment access, async work, task interop, or runtime policy.

ci NuGet License

Coherent Application Architecture

FsFlow is built around one progression:

Check -> Result -> Validation -> Flow

The same vocabulary stays the same while the execution context grows.

  • Structured Composition: A single flow {} builder that binds Result, Option, Async, Task, and ColdTask directly, eliminating the "adapter tax" of switching helper families at every boundary.
  • Architectural Honesty: Distinguishes between your Explicit Environment (business dependencies like repositories) and the Ambient Runtime (operational services like clock and logging).
  • ZIO-Style Execution: Preserves the critical distinction between typed domain failures, cancellations, and unhandled defects.
  • Composable State: Built-in Software Transactional Memory (STM) for atomic coordination across multiple variables without manual lock management.

Install

  • FsFlow for Flow and the supporting validation/runtime helpers

Example

Start with a reusable check and a fail-fast result:

open System.Threading.Tasks
open FsFlow

type RegistrationError =
    | EmailMissing
    | SaveFailed of string

let validateEmail (email: string) : Result<string, RegistrationError> =
    email
    |> Check.notBlank
    |> Check.orError EmailMissing

Use the same validation logic directly inside a task-oriented workflow:

open System.Threading.Tasks
open FsFlow

type User =
    { Email: string }

type RegistrationEnv =
    { LoadUser: int -> Task<Result<User, RegistrationError>>
      SaveUser: User -> Task<Result<unit, RegistrationError>> }

let registerUser userId : Flow<RegistrationEnv, RegistrationError, unit> =
    flow {
        let! loadUser = Flow.read _.LoadUser
        let! saveUser = Flow.read _.SaveUser

        let! user = loadUser userId
        do! validateEmail user.Email

        return! saveUser user
    }

validateEmail is just a plain Result<string, RegistrationError>. flow lifts it directly with do!. The same builder also binds Async, Task, ValueTask, and ColdTask directly.

Semantic Boundary

FsFlow is for short-circuiting, ordered workflows:

  • Check, Result, Validation, and Flow stop on the first typed failure.
  • Validation and validate {} accumulate sibling failures in a structured diagnostics graph.
  • The flow families are for orchestration, dependency access, async or task execution, and runtime concerns.

If you need accumulated validation, use Validation and validate {} explicitly instead of trying to hide it inside a flow builder.

What You Get

FsFlow stays close to standard F# and .NET:

  • flow { ... } binds to Result and Option
  • flow { ... } also binds to Async, Async<Option<_>>, Async<ValueOption<_>>, and Async<Result<_,_>>
  • flow { ... } also binds to Task, ValueTask, Task<_>, ValueTask<_>, and ColdTask
  • result {} keeps fail-fast pure code readable
  • validate {} keeps sibling validation accumulation explicit

Because tasks are hot, FsFlow includes ColdTask: a small wrapper around CancellationToken -> Task. flow handles token passing for you and keeps reruns explicit.

This is the file-oriented example shape. The full runnable example is in examples/FsFlow.ReadmeExample/Program.fs.

dotnet run --project examples/FsFlow.ReadmeExample/FsFlow.ReadmeExample.fsproj --nologo

Supporting types in the full example are just:

  • ReadmeEnv = { Root: string }
  • FileReadError = NotFound
let readTextFile (path: string) : Flow<ReadmeEnv, FileReadError, string> =
    flow {
        // In production, map access and path exceptions separately at the boundary.
        do! okIf (File.Exists path) |> orElse (NotFound path) // from Validate

        return! ColdTask(fun ct -> File.ReadAllTextAsync(path, ct)) // ColdTask<string>
    }

let program : Flow<ReadmeEnv, FileReadError, string * string> =
    flow {
        let! root = Flow.read _.Root                       // ReadmeEnv.Root -> string
        let settingsFile = Path.Combine(root, "settings.json")
        let featureFlagsFile = Path.Combine(root, "feature-flags.json")

        let! settings = readTextFile settingsFile              // Flow<ReadmeEnv, FileReadError, string>
        let! featureFlags = readTextFile featureFlagsFile      // Flow<ReadmeEnv, FileReadError, string>

        return settings, featureFlags                          // Flow<ReadmeEnv, FileReadError, string * string>
    }

It reads Root from 'env, performs two file reads in one flow {}, and keeps failure typed at the boundary.

Getting Started

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on FsFlow:

Package Downloads
FsFlow.Net

.NET task-oriented workflows, task interop, and task-specific runtime helpers for FsFlow.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.6.0 93 5/17/2026
0.5.0 99 5/17/2026
0.3.0 134 5/1/2026
0.2.0 97 4/27/2026

Folded the task-oriented workflow surface into the main FsFlow package and namespace.