ARPL 1.0.5-rc

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

ARPL (Advanced Result Pattern Library)

A lightweight C# library providing robust discriminated unions for error handling and functional programming patterns. ARPL offers two main types: Either<L,R> for generic discriminated unions and SResult<R> for specialized success/error handling.

Features 🚀

  • Either<L,R> - A generic discriminated union that can hold one of two possible types
  • SResult<R> - A specialized result type for handling success/error scenarios
  • Implicit conversions between Either<Error,R> and SResult<R>
  • Pattern matching support for elegant value handling
  • Type-safe error handling without exceptions
  • Functional programming friendly design

Type Features

Either<L,R>

  • Left(L value) - Creates a new Either instance containing a left value
  • Right(R value) - Creates a new Either instance containing a right value
  • IsLeft - Indicates if the instance contains a left value
  • IsRight - Indicates if the instance contains a right value
  • Match - Pattern matching for transforming or handling the contained value
  • MatchAsync - Asynchronous pattern matching for handling the contained value
  • Map - Transforms the right value using a mapping function (if present)
  • MapAsync - Transforms the right value using an async mapping function (if present)
  • Apply - Transforms both left and right values into a new Either
  • ApplyAsync - Asynchronously transforms both left and right values into a new Either

SResult<R>

  • Success(R value) - Creates a new success result
  • Error(Error value) - Creates a new error result
  • IsSuccess - Indicates if the result represents success
  • IsFail - Indicates if the result represents an error
  • SuccessValue - Gets the success value
  • ErrorValue - Gets the error value
  • Match - Pattern matching for transforming or handling the result
  • MatchAsync - Asynchronous pattern matching for handling the result
  • Map - Transforms the success value using a mapping function (if present)
  • MapAsync - Transforms the success value using an async mapping function (if present)
  • Apply - Transforms both error and success values into a new SResult
  • ApplyAsync - Asynchronously transforms both error and success values into a new SResult

Getting Started 🏃

Installation

Install via NuGet:

Install-Package ARPL

Basic Usage

Using Either<L,R>
using Arpl.Core;

// Create Either instances
Either<string, int> leftValue = Either<string, int>.Left("error message");
Either<string, int> rightValue = Either<string, int>.Right(42);

// Pattern match to handle both cases
leftValue.Match(
    left => Console.WriteLine($"Left value: {left}"),
    right => Console.WriteLine($"Right value: {right}"));

// Asynchronous pattern matching
await rightValue.MatchAsync(
    async left => { await Task.Delay(10); Console.WriteLine($"Left async: {left}"); return 0; },
    async right => { await Task.Delay(10); Console.WriteLine($"Right async: {right}"); return right; }
);

// Mapping right value
Either<string, string> mapped = rightValue.Map(val => $"Number: {val}");

// Async mapping
Either<string, string> asyncMapped = await rightValue.MapAsync(async val => {
    await Task.Delay(10);
    return $"Number: {val}";
});

// Chain operations with Apply
var result = either
    .Apply(
        left => Either<Error, int>.Left(left),  // Propagate error
        right => ValidateNumber(right)          // Returns Either<Error, int>
    )
    .Apply(
        left => Either<Error, string>.Left(left),  // Propagate error
        right => FormatNumber(right)              // Returns Either<Error, string>
    );

// Async operations with ApplyAsync
await either.ApplyAsync(
    async left => {
        await Task.Delay(10);
        return Either<Error, string>.Left(left);
    },
    async right => {
        await Task.Delay(10);
        return Either<Error, string>.Right($"Processed: {right}");
    }
);
Using SResult<R>
// Create success result
SResult<int> success = SResult<int>.Success(42);

// Create error result
SResult<int> error = SResult<int>.Error(new Error("Something went wrong"));

// Pattern match
var message = success.Match(
    fail => $"Error: {fail.Message}",
    value => $"Success: {value}"
);

// Asynchronous pattern matching
await error.MatchAsync(
    async fail => { await Task.Delay(10); return $"Async error: {fail.Message}"; },
    async value => { await Task.Delay(10); return $"Async success: {value}"; }
);

// Mapping success value
SResult<string> mapped = success.Map(val => $"Result: {val}");

// Async mapping
SResult<string> asyncMapped = await success.MapAsync(async val => {
    await Task.Delay(10);
    return $"Result: {val}";
});

// Chain operations with Apply
var result = sresult
    .Apply(
        error => SResult<int>.Error(error),     // Propagate error
        success => ValidateNumber(success)      // Returns SResult<int>
    )
    .Apply(
        error => SResult<string>.Error(error),  // Propagate error
        success => FormatNumber(success)        // Returns SResult<string>
    );

// Async operations with ApplyAsync
await sresult.ApplyAsync(
    async error => {
        await Task.Delay(10);
        return SResult<string>.Error(error);
    },
    async success => {
        await Task.Delay(10);
        return SResult<string>.Success($"Processed: {success}");
    }
);

// Check result type
if (success.IsSuccess)
    Console.WriteLine($"Success value: {success.SuccessValue}");
if (error.IsFail)
    Console.WriteLine($"Error value: {error.ErrorValue}");

Error Handling

ARPL provides a flexible error handling system that allows you to work with both single errors and collections of errors. The Error class serves as the base for all error types, and the ErrorCollection allows you to aggregate multiple errors together.

Single Errors

// Create a simple error
var error = Errors.New("Invalid input", "ERR001");

// Create an unexpected error from an exception
var unexpectedError = Errors.New(new Exception("Database connection failed"));

Multiple Errors

When you need to collect and combine multiple errors, use ErrorCollection:

// Start with an empty error collection
var errors = Errors.EmptyError();

// Add errors as they are found
errors.Add(Errors.New("Invalid email", "VAL001"));
errors.Add(Errors.New("Password too short", "VAL002"));

// You can also combine errors using the + operator
var error1 = Errors.New("Field required", "VAL003");
var error2 = Errors.New("Invalid format", "VAL004");
var combined = error1 + error2; // Creates a new ErrorCollection

// Use in result types
return SResult<User>.Error(errors); // Works with both single Error and ErrorCollection

Bespoke Errors

ARPL allows you to create custom error types by extending the Error class. This enables you to create domain-specific errors that carry meaningful context for your application:

public record NotFoundError : Error
{
    public NotFoundError(string entityType, string identifier)
    {
        EntityType = entityType;
        Identifier = identifier;
    }

    public string EntityType { get; }
    public string Identifier { get; }
    public override string Message => $"{EntityType} with id {Identifier} was not found";
    public override bool IsExpected => true;
}

// Usage example:
public async Task<SResult<User>> GetUserById(string userId)
{
    var user = await _repository.FindUserById(userId);
    if (user == null)
        return SResult<User>.Error(new NotFoundError("User", userId));

    return SResult<User>.Success(user);
}

// Pattern matching with custom error
var result = await GetUserById("123");
var message = result.Match(
    fail => fail is NotFoundError nf 
        ? $"Could not find {nf.EntityType} {nf.Identifier}" 
        : "Unknown error",
    success => $"Found user: {success.Name}"
);

Bespoke errors provide several benefits:

  1. Type-safe error handling with pattern matching
  2. Rich error context specific to your domain
  3. Clear distinction between expected and unexpected errors
  4. Consistent error handling across your application

Implicit Conversions

ARPL supports implicit conversions between Either<Error,R> and SResult<R>, making it seamless to work with both types:

// Convert from Either to SResult
Either<Error, int> either = Either<Error, int>.Right(42);
SResult<int> result = either; // Implicit conversion

// Convert from SResult to Either
SResult<int> sresult = SResult<int>.Success(42);
Either<Error, int> converted = sresult; // Implicit conversion

Note: The implicit conversion only works for Either<Error, R> and SResult<R>. Attempting to convert other types will throw an exception.

Best Practices

  1. Use Either<L,R> when you need a generic discriminated union
  2. Use SResult<R> for specific success/error handling scenarios
  3. Leverage pattern matching with Match for clean and safe value handling
  4. Prefer using the type system for error handling instead of exceptions

StaticFactory Helpers

For a more functional and concise style, ARPL provides the StaticFactory class, which offers utility methods to create instances of SResult and Either in a direct and expressive way:

using static Arpl.Core.StaticFactory;

// Create a success result
var success = Success(42); // SResult<int>

// Create a failure result
var fail = Fail<int>(new Error("fail")); // SResult<int>

// Create an Either with a left value
var left = Left<string, int>("error"); // Either<string, int>

// Create an Either with a right value
var right = Right<string, int>(42); // Either<string, int>

Available Methods

  • Success<T>(T value): Creates a successful SResult<T>.
  • Fail<T>(Error value): Creates a failed SResult<T>.
  • Left<L, R>(L value): Creates an Either<L, R> with a left value.
  • Right<L, R>(R value): Creates an Either<L, R> with a right value.

These methods make it easier to create values for functional flows and tests, making your code cleaner and more readable.

Contributing 🤝

Contributions are welcome! Feel free to submit issues and pull requests.

License 📄

This project is licensed under the MIT License - see the LICENSE file for details.


Disclaimer: This README was generated and reviewed with the assistance of Windsurf AI.

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

    • No dependencies.
  • net7.0

    • No dependencies.
  • net8.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on ARPL:

Package Downloads
ARPL.AspNetCore

A lightweight C# library providing robust discriminated unions for error handling and functional programming patterns. ARPL offers two main types: Either<L,R> for generic discriminated unions and SResult<R> for specialized success/error handling.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.12 32 9/28/2025
1.0.11 115 9/13/2025
1.0.10 152 5/2/2025
1.0.9 162 5/1/2025
1.0.8 184 4/30/2025
1.0.7 166 4/30/2025
1.0.6 171 4/29/2025
1.0.5 106 4/26/2025
1.0.5-rc 165 4/25/2025
1.0.4 169 4/25/2025
1.0.3 177 4/24/2025
1.0.2 121 4/19/2025
1.0.1 215 4/16/2025
1.0.0 228 4/15/2025