AliasTopan.EitherPattern 1.0.0

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

Nuget Nuget GitHub

A dead-simple, functional-inspired result library for .NET featuring a robust Either type. This library provides a clean, type-safe, and intuitive way to handle the success or failure of operations without relying on exceptions for control flow or nullable return types.

Philosophy

This library is built on a "dead-simple" philosophy. Business logic is complex enough; the tools we use to build it shouldn't be.

  • Explicit over Implicit: The library's behavior is transparent. There is no hidden magic.
  • Simple, Composable Tools: It provides a small set of simple, powerful methods (Map, Then, Peek) that can be composed to build complex, resilient workflows.
  • No Over-Engineering: The Either type does one thing and does it well: it represents one of two possible outcomes. It does not try to be a kitchen-sink solution.

Installation

Install the package from the .NET CLI:

dotnet add package AliasTopan.EitherPattern --version 1.0.0

Core Concepts

The library provides two central types: Either<TError, TSuccess> and Success.

Either<TError, TSuccess>

This is the core of the library. It's a container that holds one of two possible values:

  • An Error of type TError.
  • A Success of type TSuccess.

An Either can never hold both at the same time. This forces you to handle both the success and failure paths, preventing a whole class of bugs.

By convention, the "happy" or success path is the right-hand type parameter (TSuccess), and the alternative or error path is the left-hand type parameter (TError).

Success (struct)

A simple, lightweight struct used as the TSuccess type for operations that don't return a value (i.e., methods that would normally return void). Its presence signifies a successful operation that produced no data.

Getting Started: Basic Usage

Let's create a function that can fail, like parsing a string to an integer.

using AliasTopan.EitherPattern;

public record ParseError(string Message);

public class Parser
{
    public static Either<ParseError, int> ParseInteger(string input)
    {
        if (int.TryParse(input, out int number))
        {
            // The operation succeeded. Return a Success containing the number.
            return Either<ParseError, int>.Success(number);
        }
        else
        {
            // The operation failed. Return an Error containing a descriptive error object.
            var error = new ParseError($"Input '{input}' is not a valid integer.");
            return Either<ParseError, int>.Error(error);
        }
    }
}

To get the value out safely, you use the .Match() method, which forces you to provide a handler for both outcomes.

var successfulResult = Parser.ParseInteger("123");
var failedResult = Parser.ParseInteger("abc");

string message = successfulResult.Match(
    onSuccess: number => $"Parsed number is: {number}",
    onError:   error  => $"Failed to parse. Reason: {error.Message}"
);
// message is "Parsed number is: 123"

string errorMessage = failedResult.Match(
    onSuccess: number => $"Parsed number is: {number}",
    onError:   error  => $"Failed to parse. Reason: {error.Message}"
);
// errorMessage is "Failed to parse. Reason: Input 'abc' is not a valid integer."

Key Features

Transforming a Success Value with .Map()

Use .Map() to apply a simple, non-failable transformation to a success value. If the Either is an Error, Map does nothing.

Either<ParseError, int> eitherInt = Parser.ParseInteger("123"); // Success(123)

// Transform the int to a string message
Either<ParseError, string> eitherMessage = eitherInt
    .Map(num => num * 10) // Becomes Success(1230)
    .Map(num => $"The final value is {num}"); // Becomes Success("The final value is 1230")

// eitherMessage.ToString() -> "Success: The final value is 1230"

Chaining Failable Operations with .Then()

This is the most powerful feature. Use .Then() to chain multiple operations where each step can fail. If any step returns an Error, the entire rest of the chain is skipped.

// Step 1: Can fail
Either<IError, string> ReadFile() { /* ... */ }
// Step 2: Can fail
Either<IError, Config> ParseConfig(string text) { /* ... */ }
// Step 3: Can fail
Either<IError, Success> ConnectToDatabase(Config config) { /* ... */ }

// The workflow is a clean, flat chain:
Either<IError, Success> result = ReadFile()
    .Then(text => ParseConfig(text))
    .Then(config => ConnectToDatabase(config));

result.Match(
    onSuccess: _ => Console.WriteLine("Process completed successfully!"),
    onError: err => Console.Error.WriteLine($"Workflow failed: {err.Message}")
);

Inspecting a Value with .Peek()

Use .Peek() to perform a side-effect, like logging, without breaking the chain. It allows you to "peek" at the value inside and then returns the original Either unchanged.

var result = ReadFile()
    .Peek(text => // Inspect the success
    {
        Console.WriteLine("DEBUG: File read successfully.")
    })
    .PeekError(err => // Inspect the error
    {
        Console.Error.WriteLine($"ERROR: Could not read file: {err.Message}")
    })
    .Then(text => ParseConfig(text));

Advanced Usage

Handling Valueless Successes

For an operation that returns void on success, use the Success struct as your TSuccess type.

public record SaveError(string Message);

public Either<SaveError, Success> SaveSettings(string settings)
{
    try
    {
        // db.SaveChanges(settings);
        return Either<SaveError, Success>.Success(Success.Default);
    }
    catch (Exception ex)
    {
        return Either<SaveError, Success>.Error(new SaveError(ex.Message));
    }
}

Architectural Pattern: Defining Business Boundaries

The Either type encourages good architectural patterns like Domain-Driven Design (DDD) by allowing you to define clear boundaries for your business logic.

You can define a shared error interface for a specific workflow (a "Bounded Context"). All functions within that workflow return an Either using that common error interface.

// A common interface for all authentication errors
public interface IAuthenticationError { string Message { get; } }

public record UsernameNotFoundError(string Username) : IAuthenticationError { /* ... */ }
public record IncorrectPasswordError() : IAuthenticationError { /* ... */ }

// All functions in the workflow use the common interface in their return type.
public class AuthService
{
    private Either<IAuthenticationError, User> GetUser(string username) { /* ... */ }
    private Either<IAuthenticationError, User> VerifyPassword(User user, string password) { /* ... */ }
    private Either<IAuthenticationError, JwtToken> GenerateJwt(User user) { /* ... */ }

    // This allows for a perfectly clean and readable workflow.
    public Either<IAuthenticationError, JwtToken> Authenticate(string username, string password)
    {
        return GetUser(username)
            .Then(user => VerifyPassword(user, password))
            .Then(GenerateJwt);
    }
}

This architectural style keeps the high-level business logic simple and readable, as the error handling policy is enforced by the function signatures within the boundary.

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 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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  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.0

    • No dependencies.
  • net8.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
1.0.0 662 11/20/2025
1.0.0-preview.4 205 9/1/2025
1.0.0-preview.3 221 8/30/2025
1.0.0-preview.2 169 7/29/2025
1.0.0-preview.1 440 7/25/2025