SoftwareMadeSimple.SimpleResults.Functional 1.3.0

dotnet add package SoftwareMadeSimple.SimpleResults.Functional --version 1.3.0
                    
NuGet\Install-Package SoftwareMadeSimple.SimpleResults.Functional -Version 1.3.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="SoftwareMadeSimple.SimpleResults.Functional" Version="1.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SoftwareMadeSimple.SimpleResults.Functional" Version="1.3.0" />
                    
Directory.Packages.props
<PackageReference Include="SoftwareMadeSimple.SimpleResults.Functional" />
                    
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 SoftwareMadeSimple.SimpleResults.Functional --version 1.3.0
                    
#r "nuget: SoftwareMadeSimple.SimpleResults.Functional, 1.3.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 SoftwareMadeSimple.SimpleResults.Functional@1.3.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=SoftwareMadeSimple.SimpleResults.Functional&version=1.3.0
                    
Install as a Cake Addin
#tool nuget:?package=SoftwareMadeSimple.SimpleResults.Functional&version=1.3.0
                    
Install as a Cake Tool

SimpleResults.Functional

NuGet License: MIT

Functional extensions for SoftwareMadeSimple.SimpleResults, providing Map, Bind, MapError, BindError, Match, Unit, and applicative Lift/Apply with error accumulation.

Installation

dotnet add package SoftwareMadeSimple.SimpleResults.Functional

Usage

Map

Transform the success value of a Result without unwrapping it. If the result is a failure, the error is passed through unchanged.

Result<int, string> result = 5;

Result<string, string> mapped = result.Map(x => x.ToString());
// mapped.Value == "5"

Map calls can be chained:

Result<int, string> result = 3;

var mapped = result
    .Map(x => x + 1)
    .Map(x => x * 10);
// mapped.Value == 40

Bind

Chain operations that themselves return a Result. Unlike Map, the function passed to Bind returns a Result, which avoids double-wrapping. This short-circuits on the first failure.

Result<int, string> Validate(int x) =>
    x > 0 ? x : "must be positive";

Result<int, string> result = 5;

Result<int, string> bound = result.Bind(Validate);
// bound.Value == 5

Chain multiple operations that can each independently fail:

Result<int, string> Parse(string s) =>
    int.TryParse(s, out var n) ? n : "not a number";

Result<int, string> Validate(int x) =>
    x > 0 ? x : "must be positive";

Result<string, string> input = "42";

var bound = input
    .Bind(Parse)
    .Bind(Validate);
// bound.Value == 42

BindAsync

The async version of Bind for operations that return a Task<Result>. Two overloads are provided: one for when the source is already a Result, and one for when it is a Task<Result>, enabling seamless chaining in async pipelines.

Task<Result<int, string>> ParseAsync(string s) =>
    Task.FromResult<Result<int, string>>(
        int.TryParse(s, out var n) ? n : "not a number");

Task<Result<int, string>> ValidateAsync(int x) =>
    Task.FromResult<Result<int, string>>(
        x > 0 ? x : "must be positive");

Result<string, string> input = "42";

var result = await input
    .Bind(Parse)
    .BindAsync(ValidateAsync);
// result.Value == 42

MapError

Transform the error value of a Result, leaving a success unchanged. Useful for converting between error types at layer boundaries.

Result<int, string> result = "not found";

Result<int, int> mapped = result.MapError(e => e.Length);
// mapped.Error == 9

BindError

Recover from errors by applying a function that returns a new Result. Enables fallback strategies.

Result<int, string> TryCache(string error) =>
    error == "not found" ? 42 : error;

Result<int, string> result = "not found";

Result<int, string> recovered = result.BindError(TryCache);
// recovered.Value == 42

Chain multiple recovery attempts:

Result<int, string> FirstFallback(string _) => "first fallback failed";
Result<int, string> SecondFallback(string _) => 99;

Result<int, string> result = "original error";

var recovered = result
    .BindError(FirstFallback)
    .BindError(SecondFallback);
// recovered.Value == 99

BindErrorAsync

The async version of BindError for recovery operations that return a Task<Result>. Like BindAsync, it supports both Result and Task<Result> sources.

Task<Result<int, string>> TryCacheAsync(string error) =>
    Task.FromResult<Result<int, string>>(
        error == "not found" ? 42 : error);

Result<int, string> result = "not found";

var recovered = await result.BindErrorAsync(TryCacheAsync);
// recovered.Value == 42

Match

Collapse a Result into a single value by providing a function for each case. This is the standard way to exit the Result type.

Result<int, string> result = 42;

string message = result.Match(
    value => $"Got {value}",
    error => $"Failed: {error}");
// message == "Got 42"

Match is the natural endpoint of a functional pipeline:

Result<int, string> Validate(int x) =>
    x > 0 ? x : "must be positive";

Result<int, string> result = 5;

string output = result
    .Bind(Validate)
    .Map(x => x * 2)
    .Match(
        value => $"Result: {value}",
        error => $"Error: {error}");
// output == "Result: 10"

An Action-based overload is also available for performing side effects without returning a value:

Result<int, string> result = 42;

result.Match(
    value => Console.WriteLine($"Got {value}"),
    error => Console.WriteLine($"Failed: {error}"));
// prints "Got 42"

Unit

A type that represents the absence of a value, used in place of void since void cannot be a generic type parameter. This enables Result<Unit, E> for operations that either succeed with no meaningful value or fail.

Result<Unit, string> SaveToDatabase(Person person)
{
    // save logic
    return Unit.Default;
}

Result<Unit, string> result = SaveToDatabase(person);

string message = result.Match(
    _ => "Saved successfully",
    error => $"Failed: {error}");

Lift & Apply

Use Lift to wrap a function into a Result, then chain Apply calls to feed in each validated argument. Unlike Bind (which short-circuits on the first error), Apply runs every validation and collects all failures.

record Person(string Name, int Age);

Result<string, ValidationErrors> nameResult = ValidateName(input.Name);
Result<int, ValidationErrors> ageResult = ValidateAge(input.Age);

var result =
    LiftResult<ValidationErrors>
        .Lift((string name, int age) => new Person(name, age))
        .Apply(nameResult)
        .Apply(ageResult);

if (result.IsSuccess)
{
    Person person = result.Value;
}
else
{
    IEnumerable<ValidationError> errors = result.Error;
    // all validation errors are collected here
}

Lift supports functions with up to 16 parameters. Each additional parameter is applied with another .Apply() call:

record Address(string Street, string City, string Zip);

var result =
    LiftResult<ValidationErrors>
        .Lift((string street, string city, string zip) => new Address(street, city, zip))
        .Apply(streetResult)
        .Apply(cityResult)
        .Apply(zipResult);

API Summary

Method Signature Description
Map Result<T, E> ? Func<T, R> ? Result<R, E> Transform the success value
MapError Result<T, E> ? Func<E, F> ? Result<T, F> Transform the error value
Bind Result<T, E> ? Func<T, Result<R, E>> ? Result<R, E> Chain failable operations
BindAsync Result<T, E> ? Func<T, Task<Result<R, E>>> ? Task<Result<R, E>> Async chain failable operations
BindError Result<T, E> ? Func<E, Result<T, F>> ? Result<T, F> Recover from errors
BindErrorAsync Result<T, E> ? Func<E, Task<Result<T, F>>> ? Task<Result<T, F>> Async recover from errors
Match Result<T, E> ? Func<T, R> ? Func<E, R> ? R Eliminate the Result type
Match Result<T, E> ? Action<T> ? Action<E> ? void Perform side effects per case
Lift Func<T1, ..., R> ? Result<Func<...>, E> Lift a function into Result
Apply Result<Func<T, R>, E> ? Result<T, E> ? Result<R, E> Apply with error accumulation
Unit Value type representing void

Requirements

License

MIT

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.3.0 99 5/15/2026
1.2.0 149 4/3/2026
1.1.0 119 3/17/2026
1.0.1 110 3/12/2026
1.0.0 103 3/12/2026