Resultly 0.1.0-alpha.1
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
<PackageReference Include="Resultly" Version="0.1.0-alpha.1" />
<PackageVersion Include="Resultly" Version="0.1.0-alpha.1" />
<PackageReference Include="Resultly" />
paket add Resultly --version 0.1.0-alpha.1
#r "nuget: Resultly, 0.1.0-alpha.1"
#:package Resultly@0.1.0-alpha.1
#addin nuget:?package=Resultly&version=0.1.0-alpha.1&prerelease
#tool nuget:?package=Resultly&version=0.1.0-alpha.1&prerelease
Resultly
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:
- Make invalid states unrepresentable — errors are explicit in the type system
- Fail fast and explicitly — no hidden exceptions, all failures are values
- Compose operations — build complex logic from simple, composable parts
- 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 | Versions 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. |
-
.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 |