Skip to content

Instantly share code, notes, and snippets.

@zsmahi
Created April 2, 2024 22:16
Show Gist options
  • Save zsmahi/ab5fc89c8f27bdffd45c9fcb9666c04a to your computer and use it in GitHub Desktop.
Save zsmahi/ab5fc89c8f27bdffd45c9fcb9666c04a to your computer and use it in GitHub Desktop.
MayBeMonad
public readonly struct MayBe<T> : IEquatable<MayBe<T>> where T : class
{
private readonly T _value;
public MayBe(T value)
{
_value = value;
HasValue = !(value is null);
}
public static MayBe<T> None => new MayBe<T>(default);
public readonly bool HasValue { get; }
public T Value
{
get
{
if (!HasValue)
{
throw new InvalidOperationException();
}
return _value!;
}
}
public static implicit operator MayBe<T>(T value)
=> value is null ? None : new MayBe<T>(value);
public static bool operator !=(MayBe<T> left, MayBe<T> right)
=> !(left == right);
public static bool operator !=(MayBe<T> left, T right)
=> !(left == right);
public static bool operator !=(T left, MayBe<T> right)
=> !(left == right);
public static bool operator ==(MayBe<T> left, MayBe<T> right)
=> left.Equals(right);
public static bool operator ==(MayBe<T> left, T right)
=> left.Equals(right);
public static bool operator ==(T left, MayBe<T> right)
=> right.Equals(left);
public bool Equals(MayBe<T> other)
=> HasValue == other.HasValue && (!HasValue || _value!.Equals(other._value));
public override bool Equals(object obj)
=> obj is MayBe<T> other && Equals(other);
public override int GetHashCode()
=> HasValue ? _value!.GetHashCode() : 0;
public T GetValueOrDefault()
=> !HasValue ? default : _value;
public T GetValueOrDefault(T defaultValue)
=> HasValue ? _value! : defaultValue;
// Match method to handle both cases with actions
public TResult Match<TResult>(Func<T, TResult> some, Func<TResult> none)
=> HasValue ? some(_value) : none();
// OrElse method to provide a fallback value
public T OrElse(T fallback)
=> HasValue ? _value : fallback;
public T OrElse(Func<T> fallback)
=> HasValue ? _value : fallback();
public override string? ToString()
=> HasValue ? _value?.ToString() : "";
}
public static class MayBeExtensions
{
// Bind: Apply a function that returns a MayBe to the value inside the original MayBe
public static MayBe<TResult> Bind<T, TResult>(this MayBe<T> maybe, Func<T, MayBe<TResult>> binder)
where T : class
where TResult : class
=> maybe.HasValue ? binder(maybe.Value) : MayBe<TResult>.None;
// Filter: Apply a predicate to the value inside MayBe, returning None if the predicate is not satisfied
public static MayBe<T> Filter<T>(this MayBe<T> maybe, Func<T, bool> predicate)
where T : class
=> maybe.HasValue && predicate(maybe.Value) ? maybe : MayBe<T>.None;
// Map: Transform the value inside MayBe if it exists
public static MayBe<TResult> Map<T, TResult>(this MayBe<T> maybe, Func<T, TResult> mapper)
where T : class
where TResult : class
=> maybe.HasValue ? new MayBe<TResult>(mapper(maybe.Value)) : MayBe<TResult>.None;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment