Struct Maybe<T>
- Namespace
- REslava.Result.AdvancedPatterns
- Assembly
- REslava.Result.dll
Represents an optional value that may or may not exist. A functional programming alternative to null references.
public readonly struct Maybe<T> : IEquatable<Maybe<T>>
Type Parameters
TThe type of the optional value
- Implements
-
IEquatable<Maybe<T>>
- Inherited Members
- Extension Methods
Remarks
Maybe<T> is a type-safe way to handle optional values without using null references. It can be in one of two states: Some(T) when it contains a value, or None when it's empty.
This eliminates the need for null checks and provides a functional approach to handling potentially missing values.
// Instead of:
string? name = GetUser();
if (name != null) {
Console.WriteLine(name.ToUpper());
}
// Use Maybe:
Maybe<string> name = GetUser();
var upperName = name.Map(n => n.ToUpper());
Properties
HasValue
Gets whether this Maybe has a value.
public bool HasValue { get; }
Property Value
- bool
trueif this Maybe contains a value; otherwise,false.
Examples
Maybe<string> maybe = Maybe<string>.Some("hello");
if (maybe.HasValue) {
Console.WriteLine("Has value");
}
Remarks
Use this property to check if the Maybe contains a value before accessing it. This is safer than checking for null.
None
Creates a Maybe without a value (None).
public static Maybe<T> None { get; }
Property Value
- Maybe<T>
A Maybe representing the absence of a value.
Examples
Maybe<string> empty = Maybe<string>.None;
Maybe<int> noNumber = Maybe<int>.None;
Remarks
This is the singleton instance representing an empty Maybe. Use this when you want to represent the absence of a value.
Value
Gets the value if it exists, throws if not.
public T Value { get; }
Property Value
- T
The contained value.
Examples
Maybe<int> maybe = Maybe<int>.Some(42);
int value = maybe.Value; // Returns 42
Maybe<int> empty = Maybe<int>.None;
int value2 = empty.Value; // Throws InvalidOperationException
Remarks
Only access this property when you're certain the Maybe contains a value. Consider using ValueOrDefault(T) or Match<TResult>(Func<T, TResult>, Func<TResult>) for safer access.
Exceptions
- InvalidOperationException
Thrown when the Maybe has no value.
Methods
Bind<TResult>(Func<T, Maybe<TResult>>)
Chains operations that return Maybe.
public Maybe<TResult> Bind<TResult>(Func<T, Maybe<TResult>> binder)
Parameters
Returns
- Maybe<TResult>
The result of the binder function, or None if the original was None.
Type Parameters
TResultThe type of the result Maybe.
Examples
Maybe<int> userId = Maybe<int>.Some(123);
Maybe<string> userName = userId.Bind(id => FindUserById(id));
Maybe<int> invalidId = Maybe<int>.None;
Maybe<string> noUser = invalidId.Bind(id => FindUserById(id)); // None
Remarks
Bind (also known as flatMap or chain) allows you to chain operations that return Maybe. This is useful for sequential operations where each step might fail.
Exceptions
- ArgumentNullException
Thrown when binder is null.
Equals(Maybe<T>)
Indicates whether the current object is equal to another object of the same type.
public bool Equals(Maybe<T> other)
Parameters
otherMaybe<T>An object to compare with this object.
Returns
Equals(object?)
Indicates whether this instance and a specified object are equal.
public override bool Equals(object? obj)
Parameters
objobjectThe object to compare with the current instance.
Returns
- bool
true if
objand this instance are the same type and represent the same value; otherwise, false.
Filter(Func<T, bool>)
Keeps the value only if it satisfies the predicate.
public Maybe<T> Filter(Func<T, bool> predicate)
Parameters
Returns
- Maybe<T>
The original Maybe if the predicate is true, otherwise None.
Examples
Maybe<int> number = Maybe<int>.Some(10);
Maybe<int> positive = number.Filter(n => n > 0); // Some(10)
Maybe<int> large = number.Filter(n => n > 100); // None
Maybe<int> empty = Maybe<int>.None;
Maybe<int> stillEmpty = empty.Filter(n => n > 0); // None
Remarks
Filter allows you to conditionally keep or discard the value. If the Maybe is None, the predicate is not evaluated and None is returned.
Exceptions
- ArgumentNullException
Thrown when predicate is null.
GetHashCode()
Returns the hash code for this instance.
public override int GetHashCode()
Returns
- int
A 32-bit signed integer that is the hash code for this instance.
Map<TResult>(Func<T, TResult>)
Transforms the value if it exists.
public Maybe<TResult> Map<TResult>(Func<T, TResult> mapper)
Parameters
mapperFunc<T, TResult>The function to apply to the value if it exists.
Returns
- Maybe<TResult>
A new Maybe containing the transformed value, or None if the original was None.
Type Parameters
TResultThe type of the transformed value.
Examples
Maybe<int> number = Maybe<int>.Some(5);
Maybe<string> text = number.Map(n => $"Number: {n}"); // Some("Number: 5")
Maybe<int> empty = Maybe<int>.None;
Maybe<string> noText = empty.Map(n => $"Number: {n}"); // None
Remarks
Map allows you to transform the contained value without unwrapping the Maybe. If the Maybe is None, the mapper function is not called and None is returned.
Exceptions
- ArgumentNullException
Thrown when mapper is null.
Match<TResult>(Func<T, TResult>, Func<TResult>)
Pattern matching - executes the appropriate function.
public TResult Match<TResult>(Func<T, TResult> some, Func<TResult> none)
Parameters
someFunc<T, TResult>The function to execute when the Maybe has a value.
noneFunc<TResult>The function to execute when the Maybe has no value.
Returns
- TResult
The result of the executed function.
Type Parameters
TResultThe type of the result.
Examples
Maybe<int> number = Maybe<int>.Some(42);
string message = number.Match(
some: n => $"The number is {n}",
none: () => "No number available"
); // "The number is 42"
Maybe<int> empty = Maybe<int>.None;
string message2 = empty.Match(
some: n => $"The number is {n}",
none: () => "No number available"
); // "No number available"
Remarks
Match provides a way to handle both cases (Some and None) in a type-safe manner. This is similar to pattern matching in functional languages.
Exceptions
- ArgumentNullException
Thrown when some or none is null.
Some(T)
Creates a Maybe with a value (Some).
public static Maybe<T> Some(T value)
Parameters
valueTThe value to wrap in a Maybe.
Returns
- Maybe<T>
A Maybe containing the specified value.
Examples
Maybe<string> name = Maybe<string>.Some("Alice");
Maybe<int> age = Maybe<int>.Some(25);
Maybe<string?> nullable = Maybe<string?>.Some(null);
Remarks
This is the factory method for creating a Maybe that contains a value. The value can be null, in which case it will still be wrapped in a Some.
Tap(Action<T>)
Executes an action if the value exists.
public Maybe<T> Tap(Action<T> action)
Parameters
actionAction<T>The action to execute with the contained value.
Returns
- Maybe<T>
The original Maybe for method chaining.
Examples
Maybe<string> name = Maybe<string>.Some("Alice");
var result = name
.Tap(n => Console.WriteLine($"Processing: {n}"))
.Map(n => n.ToUpper());
// Output: "Processing: Alice"
// result: Some("ALICE")
Remarks
Tap is useful for side effects like logging without breaking the Maybe chain. The Maybe is returned unchanged, allowing further chaining.
Exceptions
- ArgumentNullException
Thrown when action is null.
TapNone(Action)
Executes an action if the value doesn't exist.
public Maybe<T> TapNone(Action action)
Parameters
actionActionThe action to execute when the Maybe has no value.
Returns
- Maybe<T>
The original Maybe for method chaining.
Examples
Maybe<string> name = Maybe<string>.None;
var result = name
.TapNone(() => Console.WriteLine("Using default name"))
.ValueOrDefault("Default");
// Output: "Using default name"
// result: "Default"
Remarks
TapNone is useful for handling the None case with side effects like logging defaults. The Maybe is returned unchanged, allowing further chaining.
Exceptions
- ArgumentNullException
Thrown when action is null.
ToString()
Converts to string for debugging.
public override string ToString()
Returns
- string
A string representation of the Maybe.
Examples
Maybe<string> name = Maybe<string>.Some("Alice");
Console.WriteLine(name.ToString()); // "Some(Alice)"
Maybe<int> empty = Maybe<int>.None;
Console.WriteLine(empty.ToString()); // "None"
Remarks
Returns "Some(value)" when the Maybe contains a value, or "None" when empty. This is primarily useful for debugging and logging.
ValueOrDefault(T)
Gets the value or returns a default.
public T ValueOrDefault(T defaultValue = default)
Parameters
defaultValueTThe default value to return when no value exists.
Returns
- T
The contained value or the specified default.
Examples
Maybe<string> name = Maybe<string>.Some("Alice");
string result1 = name.ValueOrDefault("Default"); // "Alice"
Maybe<string> empty = Maybe<string>.None;
string result2 = empty.ValueOrDefault("Default"); // "Default"
Remarks
This is a safe way to extract the value without throwing exceptions. It's equivalent to the null-coalescing operator (??) for Maybe.
Operators
operator ==(Maybe<T>, Maybe<T>)
public static bool operator ==(Maybe<T> left, Maybe<T> right)
Parameters
Returns
implicit operator Maybe<T>(T)
Implicit conversion from T to Maybe<T>.
public static implicit operator Maybe<T>(T value)
Parameters
valueTThe value to convert to Maybe.
Returns
- Maybe<T>
A Maybe containing the specified value.
Examples
Maybe<string> name = "Alice"; // Implicit conversion
Maybe<int> number = 42; // Implicit conversion
// These are equivalent to:
Maybe<string> name2 = Maybe<string>.Some("Alice");
Maybe<int> number2 = Maybe<int>.Some(42);
Remarks
This allows automatic conversion from any value to Maybe, making the code more concise. Null values will be wrapped in Some(null), not converted to None.
operator !=(Maybe<T>, Maybe<T>)
public static bool operator !=(Maybe<T> left, Maybe<T> right)