LeanResults 0.1.0

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

LeanResults

A lightweight Result pattern for .NET with composable sync and async mapping.

Version 0.1.0 supports Result<T>, Error, Map, and Bind. More combinators (Tap, Match, …) can be added later without changing the core model.

Installation

dotnet add package LeanResults

Or pack and install locally:

dotnet pack src/LeanResults/LeanResults.csproj -c Release
dotnet add package LeanResults --source ./src/LeanResults/bin/Release

Basic usage

using LeanResults;

Result<Order> result = order;

// Error converts implicitly to Result<T>.Failure(...)
Result<Order> missing = new Error(
    "Order.NotFound",
    "The order could not be found.");

Inspect the outcome:

if (result.IsSuccess)
{
    Console.WriteLine(result.Value);
}
else
{
    Console.WriteLine($"{result.Error!.Code}: {result.Error.Message}");
}

Sync Map

Map transforms a successful value into another value (which is wrapped in Result<TOut>):

Result<Order> result = Result<Order>.Success(order);

Result<OrderDto> dto = result.Map(order =>
    new OrderDto(order.Id, order.Total));

Async Map

Map over an already-available Result<T> with an async function:

Result<OrderDto> dto = await result.Map(async order =>
{
    var customer = await GetCustomerAsync(order.CustomerId);
    return new OrderDto(order.Id, customer.Name, order.Total);
});

Chaining from Task<Result<T>>

Map also works directly on Task<Result<T>>, so async pipelines stay fluent:

Result<OrderDto> result = await GetOrderAsync(id)
    .Map(order =>
        new OrderDto(order.Id, order.Total));

With an async mapper:

Result<OrderDto> result = await GetOrderAsync(id)
    .Map(async order =>
    {
        var customer = await GetCustomerAsync(order.CustomerId);

        return new OrderDto(
            order.Id,
            customer.Name,
            order.Total);
    });

Bind

Bind (flat-map) chains an operation that already returns Result<T>. Unlike Map, the binder's result is returned as-is — no extra wrapping.

Result<Order> charged = CreateOrder(request)
    .Bind(order => ChargeOrder(order));

Method groups work too:

Result<Order> charged = CreateOrder(request)
    .Bind(ChargeOrder);

Async binder / Task<Result<T>>:

Result<string> shipped = await CreateOrderAsync(request)
    .Bind(ChargeOrderAsync)
    .Map(order => $"Shipped {order.Id}");

Use Map when the next step returns a plain value.
Use Bind when the next step returns Result<T> (and can fail).

Failure propagation

If the result is a failure, the mapper/binder is not invoked. The same Error instance is propagated to Result<TOut>.Failure(...):

Result<Order> failed = new Error("Order.NotFound", "The order could not be found.");

Result<OrderDto> mapped = failed.Map(order =>
    new OrderDto(order.Id, order.Total));

// mapped.IsFailure == true
// mapped.Error is the same Order.NotFound error
// the mapper never ran

Exceptions thrown inside a mapper or binder are not converted to Result failures — they propagate normally.

Null handling

API Null policy
Result<T>.Failure(error) error must not be null
new Error(code, message) code and message must not be null
Map / Bind mapper/binder (and the receiver) must not be null
Result<T>.Success(value) value may be null for reference / nullable types

Why allow Success(null)?
A successful outcome can legitimately carry no payload (for example Result<string?> after an optional lookup). Rejecting null would force awkward sentinel values and fight nullable reference types. Structural consistency is still guaranteed: success never has an Error, failure never has a meaningful Value.

Prefer Result<T> when T should be non-null, and Result<T?> when null is a valid success value.

Building, testing, and packing

dotnet restore
dotnet build
dotnet test
dotnet pack -c Release

The NuGet package is written to src/LeanResults/bin/Release/.

Project layout

LeanResults/
├── LeanResults.sln
├── README.md
├── src/
│   └── LeanResults/
│       ├── LeanResults.csproj
│       ├── Result.cs
│       ├── Error.cs
│       └── ResultExtensions.cs
└── tests/
    └── LeanResults.Tests/
        ├── LeanResults.Tests.csproj
        ├── ResultTests.cs
        ├── MapTests.cs
        └── BindTests.cs

Package metadata placeholders

Before publishing, replace the placeholders in LeanResults.csproj:

  • YOUR_NAME — package authors
  • https://github.com/YOUR_USERNAME/LeanResults — repository URL
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.
  • net10.0

    • No dependencies.

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
0.1.0 114 8/5/2026