Table of Contents

Either<TLeft, TRight> Struct

Definition

Namespace
Bodu.Functional
Assembly
Bodu.Core.dll
Package
Bodu.Core 1.0.1
Source
Either{T,T}.Factories.cs

Represents a disjoint union of two possibilities: either Left(value) carrying a non-null value of type TLeft, or Right(value) carrying a non-null value of type TRight.

public readonly struct Either<TLeft, TRight> : IEquatable<Either<TLeft, TRight>>

Type Parameters

TLeft

The type of the value carried on the left side.

TRight

The type of the value carried on the right side.

Implements
IEquatable<Either<TLeft, TRight>>
Inherited Members
Extension Methods

Remarks

Either<TLeft, TRight> is a symmetric choice type: neither side is privileged, and no side carries a success or failure connotation - Result<T> owns the railway-oriented bias. Use it when a value is legitimately one of two shapes (for example, a parsed literal that is either a number or a string). Project each side independently with MapLeft<TResult>(Func<TLeft, TResult>) and MapRight<TResult>(Func<TRight, TResult>), exchange the sides with Swap(), and collapse to a single value with Match<TResult>(Func<TLeft, TResult>, Func<TRight, TResult>).

default(Either<TLeft, TRight>) carries neither side. On an uninitialized instance IsLeft and IsRight are both false, TryGetLeft(out TLeft) and TryGetRight(out TRight) return false, and every operation that requires a side - Match, MapLeft, MapRight, and Swap - throws InvalidOperationException.

The type throws on default rather than defaulting to a side because no privileged side exists to default to - unlike Option<T>, whose default is None, and Result<T>, whose default is a failure carrying the empty error. Silently treating default as Left(default!) would fabricate a value the Left(TLeft) factory refuses to accept. This mirrors the System.Collections.Immutable.ImmutableArray`1.IsDefault precedent for a struct whose uninitialized state is observable but unusable.

Properties

IsLeft

Gets a value indicating whether this either carries a left value.

public bool IsLeft { get; }

Property Value

bool

true if the left side is active; otherwise, false - including for default(Either<TLeft, TRight>), which carries neither side.

Remarks

The type deliberately exposes no throwing value accessor; retrieve the active side's value through TryGetLeft(out TLeft), TryGetRight(out TRight), or Match<TResult>(Func<TLeft, TResult>, Func<TRight, TResult>).

IsRight

Gets a value indicating whether this either carries a right value.

public bool IsRight { get; }

Property Value

bool

true if the right side is active; otherwise, false - including for default(Either<TLeft, TRight>), which carries neither side.

Remarks

The type deliberately exposes no throwing value accessor; retrieve the active side's value through TryGetLeft(out TLeft), TryGetRight(out TRight), or Match<TResult>(Func<TLeft, TResult>, Func<TRight, TResult>).

Methods

Equals(Either<TLeft, TRight>)

Determines whether the specified either is equal to the current instance.

public bool Equals(Either<TLeft, TRight> other)

Parameters

other Either<TLeft, TRight>

The either to compare with this instance.

Returns

bool

true if both instances carry the same side and the active side's values compare equal using Default, or both are uninitialized; otherwise, false.

Remarks

The side participates in equality: Left(x) and Right(x) are never equal, even when TLeft and TRight are the same type and the values compare equal. Two uninitialized (default) instances compare equal.

Equals(object?)

Determines whether the specified object is equal to the current either.

public override bool Equals(object? obj)

Parameters

obj object

The object to compare. Only another Either<TLeft, TRight> can be equal; all other types, including null, return false.

Returns

bool

true if obj is an Either<TLeft, TRight> equal to this instance; otherwise, false.

GetHashCode()

Returns a hash code for the current instance consistent with Equals(Either<TLeft, TRight>).

public override int GetHashCode()

Returns

int

A hash code combining the active side with the active side's value, so that Left(x) and Right(x) hash differently where possible; 0 for an uninitialized instance.

Left(TLeft)

Creates an either carrying the specified non-null left value.

public static Either<TLeft, TRight> Left(TLeft value)

Parameters

value TLeft

The left value to wrap. Must not be null.

Returns

Either<TLeft, TRight>

An Either<TLeft, TRight> whose IsLeft is true.

Examples

var left = Either<int, string>.Left(42);        // Left(42)
var right = Either<int, string>.Right("text");  // Right(text)

Remarks

The factory rejects null because a side that is present must carry a usable value; absence is modelled by Option<T>, not by a null side.

Exceptions

ArgumentNullException

value is null.

MapLeft<TResult>(Func<TLeft, TResult>)

Projects the left value using the specified selector, passing a right value through untouched.

public Either<TResult, TRight> MapLeft<TResult>(Func<TLeft, TResult> selector)

Parameters

selector Func<TLeft, TResult>

The projection applied to the left value when the left side is active.

Returns

Either<TResult, TRight>

Left(selector(value)) when the left side is active; otherwise a right-carrying either with the original right value.

Type Parameters

TResult

The left type produced by the selector.

Examples

var doubled = Either<int, string>.Left(21).MapLeft(l => l * 2);   // Left(42)
var same = Either<int, string>.Right("text").MapLeft(l => l * 2); // Right(text) - selector not invoked

Remarks

The selector is not invoked when the right side is active. The lift is strict: a null projection result is rejected by the Left(TLeft) factory with ArgumentNullException.

Exceptions

ArgumentNullException

selector is null.

InvalidOperationException

The either is default(Either<TLeft, TRight>) and carries neither side.

MapRight<TResult>(Func<TRight, TResult>)

Projects the right value using the specified selector, passing a left value through untouched.

public Either<TLeft, TResult> MapRight<TResult>(Func<TRight, TResult> selector)

Parameters

selector Func<TRight, TResult>

The projection applied to the right value when the right side is active.

Returns

Either<TLeft, TResult>

Right(selector(value)) when the right side is active; otherwise a left-carrying either with the original left value.

Type Parameters

TResult

The right type produced by the selector.

Remarks

The selector is not invoked when the left side is active. The lift is strict: a null projection result is rejected by the Right(TRight) factory with ArgumentNullException.

Exceptions

ArgumentNullException

selector is null.

InvalidOperationException

The either is default(Either<TLeft, TRight>) and carries neither side.

Match(Action<TLeft>, Action<TRight>)

Invokes the action for the active side of the either.

public void Match(Action<TLeft> onLeft, Action<TRight> onRight)

Parameters

onLeft Action<TLeft>

The action invoked with the left value when the left side is active.

onRight Action<TRight>

The action invoked with the right value when the right side is active.

Exceptions

ArgumentNullException

onLeft or onRight is null.

InvalidOperationException

The either is default(Either<TLeft, TRight>) and carries neither side.

Match<TResult>(Func<TLeft, TResult>, Func<TRight, TResult>)

Collapses the either to a single value by invoking the branch for the active side.

public TResult Match<TResult>(Func<TLeft, TResult> onLeft, Func<TRight, TResult> onRight)

Parameters

onLeft Func<TLeft, TResult>

The projection invoked with the left value when the left side is active.

onRight Func<TRight, TResult>

The projection invoked with the right value when the right side is active.

Returns

TResult

The value produced by the invoked branch.

Type Parameters

TResult

The type produced by both branches.

Examples

var label = either.Match(l => $"number {l}", r => $"text {r}");

Exceptions

ArgumentNullException

onLeft or onRight is null.

InvalidOperationException

The either is default(Either<TLeft, TRight>) and carries neither side.

Right(TRight)

Creates an either carrying the specified non-null right value.

public static Either<TLeft, TRight> Right(TRight value)

Parameters

value TRight

The right value to wrap. Must not be null.

Returns

Either<TLeft, TRight>

An Either<TLeft, TRight> whose IsRight is true.

Remarks

The factory rejects null because a side that is present must carry a usable value; absence is modelled by Option<T>, not by a null side.

Exceptions

ArgumentNullException

value is null.

Swap()

Exchanges the sides of the either.

public Either<TRight, TLeft> Swap()

Returns

Either<TRight, TLeft>

An Either<TLeft, TRight> carrying the same value on the opposite side: a left value becomes the right value and a right value becomes the left value.

Remarks

Swapping twice yields an either equal to the original.

Exceptions

InvalidOperationException

The either is default(Either<TLeft, TRight>) and carries neither side.

ToString()

Returns a string representation of the either.

public override string ToString()

Returns

string

"Left(value)" when the left side is active, "Right(value)" when the right side is active, or "Either()" for an uninitialized instance.

TryGetLeft(out TLeft)

Attempts to retrieve the left value.

public bool TryGetLeft(out TLeft value)

Parameters

value TLeft

When this method returns, the left value if the left side is active; otherwise the default value.

Returns

bool

true if the left side is active; otherwise, false.

Remarks

Returns false - and never throws - for a right-carrying either and for default(Either<TLeft, TRight>).

TryGetRight(out TRight)

Attempts to retrieve the right value.

public bool TryGetRight(out TRight value)

Parameters

value TRight

When this method returns, the right value if the right side is active; otherwise the default value.

Returns

bool

true if the right side is active; otherwise, false.

Remarks

Returns false - and never throws - for a left-carrying either and for default(Either<TLeft, TRight>).

Operators

operator ==(Either<TLeft, TRight>, Either<TLeft, TRight>)

Determines whether two eithers are equal.

public static bool operator ==(Either<TLeft, TRight> left, Either<TLeft, TRight> right)

Parameters

left Either<TLeft, TRight>

The first either to compare.

right Either<TLeft, TRight>

The second either to compare.

Returns

bool

true if both carry the same side with values that compare equal using the default equality comparer, or both are uninitialized; otherwise, false.

operator !=(Either<TLeft, TRight>, Either<TLeft, TRight>)

Determines whether two eithers are not equal.

public static bool operator !=(Either<TLeft, TRight> left, Either<TLeft, TRight> right)

Parameters

left Either<TLeft, TRight>

The first either to compare.

right Either<TLeft, TRight>

The second either to compare.

Returns

bool

true if the eithers differ; otherwise, false.

Applies to

ProductVersions
.NET8, 10