Resultly 0.1.0-alpha.1

This is a prerelease version of Resultly.
There is a newer version of this package available.
See the version list below for details.
dotnet add package Resultly --version 0.1.0-alpha.1
                    
NuGet\Install-Package Resultly -Version 0.1.0-alpha.1
                    
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="Resultly" Version="0.1.0-alpha.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Resultly" Version="0.1.0-alpha.1" />
                    
Directory.Packages.props
<PackageReference Include="Resultly" />
                    
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 Resultly --version 0.1.0-alpha.1
                    
#r "nuget: Resultly, 0.1.0-alpha.1"
                    
#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 Resultly@0.1.0-alpha.1
                    
#: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=Resultly&version=0.1.0-alpha.1&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Resultly&version=0.1.0-alpha.1&prerelease
                    
Install as a Cake Tool

Resultly

Build Status NuGet NuGet Downloads License: MIT .NET

High-performance functional Result types for .NET — explicit error handling without exceptions.

Resultly implements Railway Oriented Programming, providing type-safe error handling through discriminated unions. It eliminates the need for try-catch blocks and makes error paths explicit in your type system.

Features

  • 🚀 Zero allocation — struct-based design with aggressive inlining
  • 🔒 Type-safe — closed type hierarchy with exhaustive pattern matching
  • 🛤️ Railway Oriented Programming — compose operations that short-circuit on errors
  • Async/await support — full async variants of all operations
  • 📦 Collection operations — Traverse, Sequence, Combine for working with multiple Results
  • 🧩 Monadic — Map, Bind, Then, and other functional operators
  • 🎯 Validation — Ensure and EnsureAll for domain validation
  • 🔄 Error recovery — OrElse, Recover, and fallback mechanisms
  • 📘 Comprehensive — extensive XML documentation

Installation

dotnet add package Resultly

Quick Start

using Resultly;

// Create Results
Result<int, string> success = Result<int, string>.Success(42);
Result<int, string> failure = Result<int, string>.Failure("Not found");

// Or use implicit conversions
Result<int, string> result = 42;           // Success
Result<int, string> error = "Not found";  // Failure

// Pattern matching
var message = result.Match(
    onSuccess: value => $"Got {value}",
    onError: error => $"Error: {error}"
);

Railway Oriented Programming

Chain operations that automatically short-circuit on the first error:

var result = ValidateInput(input)
    .Then(ParseValue)
    .Ensure(x => x > 0, _ => "Must be positive")
    .Map(x => x * 2)
    .Then(SaveToDatabase)
    .MapError(error => $"Pipeline failed: {error}");

// Handle the result
result.Match(
    onSuccess: id => Console.WriteLine($"Saved with ID: {id}"),
    onError: error => Console.WriteLine($"Error: {error}")
);

Core Operations

Map

Transform success values while preserving errors:

Result<int, string> result = 42;
var doubled = result.Map(x => x * 2);  // Ok(84)

Bind (flatMap)

Chain Result-returning functions without nesting:

Result<string, string> GetUserId(string email) { /* ... */ }
Result<User, string> LoadUser(string userId) { /* ... */ }

var user = GetUserId("user@example.com")
    .Bind(LoadUser);

Then

Railway-style continuation (alias for Bind with clearer intent):

var result = ParseInput(input)
    .Then(Validate)
    .Then(Transform)
    .Then(Persist);

Ensure

Validate and convert to error if predicate fails:

var result = Result<int, string>.Success(150)
    .Ensure(x => x >= 0, _ => "Must be non-negative")
    .Ensure(x => x <= 100, x => $"{x} exceeds maximum");

Match

Exhaustively handle both cases:

var output = result.Match(
    onSuccess: value => ProcessValue(value),
    onError: error => HandleError(error)
);

Async Support

All operations have async variants:

var result = await ValidateAsync(input)
    .ThenAsync(async x => await SaveAsync(x))
    .MapAsync(async x => await TransformAsync(x));

Collection Operations

Sequence

Combine multiple Results, short-circuiting on first error:

var results = new[]
{
    ParseInt("1"),
    ParseInt("2"),
    ParseInt("3")
};

Result<IReadOnlyList<int>, string> combined = results.Sequence();
// Ok([1, 2, 3])

Traverse

Apply a Result-returning function to each element:

var numbers = new[] { "1", "2", "3" };
var result = numbers.Traverse(ParseInt);
// Ok([1, 2, 3])

Combine

Collect all errors instead of short-circuiting:

var results = validations.Combine();
// Returns all errors if any validation fails

Error Handling

MapError

Transform error types:

var result = operation()
    .MapError(ex => new ValidationError(ex.Message));

OrElse

Provide alternative on error:

var result = TryPrimarySource()
    .OrElse(error => TrySecondarySource());

Recover

Convert errors to success values:

var result = RiskyOperation()
    .Recover(error => DefaultValue);

Practical Example

public record User(int Id, string Email, int Age);
public record ValidationError(string Field, string Message);

public Result<User, ValidationError> CreateUser(string email, int age)
{
    return ValidateEmail(email)
        .Bind(validEmail => ValidateAge(age)
            .Map(validAge => new User(0, validEmail, validAge)))
        .Then(SaveUser);
}

Result<string, ValidationError> ValidateEmail(string email)
{
    return email.Contains("@")
        ? Result<string, ValidationError>.Success(email)
        : Result<string, ValidationError>.Failure(
            new ValidationError("Email", "Invalid format"));
}

Result<int, ValidationError> ValidateAge(int age)
{
    return age >= 18
        ? Result<int, ValidationError>.Success(age)
        : Result<int, ValidationError>.Failure(
            new ValidationError("Age", "Must be 18 or older"));
}

Result<User, ValidationError> SaveUser(User user)
{
    // Database operation
    return Result<User, ValidationError>.Success(user with { Id = 123 });
}

// Usage
var result = CreateUser("user@example.com", 25);
result.Match(
    onSuccess: user => Console.WriteLine($"Created user {user.Id}"),
    onError: error => Console.WriteLine($"{error.Field}: {error.Message}")
);

ASP.NET Core Integration

[HttpPost]
public IActionResult CreateUser([FromBody] CreateUserRequest request)
{
    var result = ValidateRequest(request)
        .Then(CreateUserFromRequest)
        .Then(SaveToDatabase);

    return result.Match(
        onSuccess: user => Ok(new { userId = user.Id }),
        onError: error => BadRequest(new { error = error.Message })
    );
}

Philosophy

Resultly embraces these principles:

  1. Make invalid states unrepresentable — errors are explicit in the type system
  2. Fail fast and explicitly — no hidden exceptions, all failures are values
  3. Compose operations — build complex logic from simple, composable parts
  4. Railway Oriented Programming — success and failure paths are separate tracks

Performance

Resultly is designed for zero-allocation scenarios:

  • Struct-based types where possible
  • Aggressive inlining via [MethodImpl]
  • No reflection or dynamic dispatch
  • Minimal boxing/unboxing

Comparison with Exceptions

Traditional exception handling:

try
{
    var user = GetUser(id);
    var validated = ValidateUser(user);
    var saved = SaveUser(validated);
    return saved;
}
catch (NotFoundException ex)
{
    // Handle not found
}
catch (ValidationException ex)
{
    // Handle validation
}
catch (DatabaseException ex)
{
    // Handle database error
}

With Resultly:

return GetUser(id)
    .Then(ValidateUser)
    .Then(SaveUser)
    .MapError(error => /* handle all errors uniformly */);

License

MIT License - see LICENSE for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Credits

Inspired by:

  • F# Result type
  • Rust's Result<T, E>
  • Railway Oriented Programming by Scott Wlaschin
  • Functional programming patterns in Haskell and Scala
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 is compatible.  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.
  • .NETStandard 2.1

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.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.7.0 264 11/13/2025
0.5.0 258 11/13/2025
0.4.0 260 11/13/2025
0.3.0 260 11/13/2025
0.2.4 254 11/11/2025
0.2.3 184 11/9/2025
0.2.0-alpha.8 146 11/9/2025
0.2.0-alpha.7 149 11/9/2025
0.2.0-alpha.6 144 11/9/2025
0.2.0-alpha.5 145 11/3/2025
0.2.0-alpha.4 133 11/3/2025
0.2.0-alpha.2 134 11/2/2025
0.2.0-alpha.1 142 11/2/2025
0.1.0-alpha.3 74 11/2/2025
0.1.0-alpha.1 75 11/2/2025