Skip to content

Instantly share code, notes, and snippets.

@htsign
Last active July 21, 2021 13:58
Show Gist options
  • Save htsign/0c62e3776085454254ef268bbf851903 to your computer and use it in GitHub Desktop.
Save htsign/0c62e3776085454254ef268bbf851903 to your computer and use it in GitHub Desktop.
simple C# Option(Maybe) MonadLike implementation
public static class Option
{
public static Option<T> Create<T>(T? value) => value != null ? new Some<T>(value) : Option<T>.None;
public static None<dynamic> None { get; } = new None<dynamic>();
}
public abstract class Option<T> : IEquatable<Option<T>>
{
public static Option<T> None { get; } = new None<T>();
public abstract bool IsSome { get; }
public abstract bool IsNone { get; }
public abstract Option<U> Select<U>(Func<T, U> f);
public abstract Option<T> Where(Func<T, bool> f);
public abstract Option<U> SelectMany<U>(Func<T, Option<U>> f);
public abstract Option<V> SelectMany<U, V>(Func<T, Option<U>> f, Func<T, U, V> g);
public abstract T OrDefault(T defaultValue);
public abstract T OrDefault(Func<T> defaultFunc);
public static bool operator ==(Option<T>? lhs, Option<T>? rhs) => lhs?.Equals(rhs) ?? rhs == null;
public static bool operator !=(Option<T>? lhs, Option<T>? rhs) => !(lhs == rhs);
public override bool Equals(object? other) => other is Option<T> opt ? Equals(opt) : false;
public bool Equals(Option<T>? other) => (this, other) switch
{
(_, null) => false,
(Some<T>(var val1), Some<T>(var val2)) => object.Equals(val1, val2),
(None<T>(), None<T>()) => true,
_ => false,
};
public override int GetHashCode() => this switch
{
Some<T>(var value) => value?.GetHashCode() ?? 0,
_ => 0,
};
}
public sealed class Some<T> : Option<T>
{
public T Value { get; }
public override bool IsSome { get; } = true;
public override bool IsNone { get; } = false;
internal Some(T value) => Value = value;
public override Option<U> Select<U>(Func<T, U> f) => Option.Create(f(Value));
public override Option<T> Where(Func<T, bool> f) => f(Value) ? this : None;
public override Option<U> SelectMany<U>(Func<T, Option<U>> f) => f(Value);
public override Option<V> SelectMany<U, V>(Func<T, Option<U>> f, Func<T, U, V> g) =>
SelectMany(x => f(x).SelectMany(y => Option.Create(g(x, y))));
public override T OrDefault(T _) => Value;
public override T OrDefault(Func<T> _) => Value;
public override string ToString() => $"Some({Value})";
public void Deconstruct(out T value) => value = Value;
}
public sealed class None<T> : Option<T>
{
public override bool IsSome { get; } = false;
public override bool IsNone { get; } = true;
internal None() { }
public override Option<U> Select<U>(Func<T, U> f) => new None<U>();
public override Option<T> Where(Func<T, bool> f) => this;
public override Option<U> SelectMany<U>(Func<T, Option<U>> f) => new None<U>();
public override Option<V> SelectMany<U, V>(Func<T, Option<U>> f, Func<T, U, V> g) => new None<V>();
public override T OrDefault(T defaultValue) => defaultValue;
public override T OrDefault(Func<T> defaultFunc) => defaultFunc();
public override string ToString() => "None";
public void Deconstruct() { }
public static implicit operator None<T>(None<dynamic>? none) => new None<T>();
public static implicit operator None<dynamic>(None<T>? none) => Option.None;
}
class Sample
{
static void Main()
{
var concatinated =
from x in Option.Create("Hello")
from y in Option.Create("World")
select $"{x}, {y}!";
Console.WriteLine(concatinated.OrDefault("(failed)"));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment