Created
December 1, 2024 12:29
-
-
Save adammyhre/253ecd375e2a17974c496b12e2ab54f0 to your computer and use it in GitHub Desktop.
Monads, Nullables, and the Power of Optionals in Unity
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
public struct Either<TLeft, TRight> { | |
readonly TLeft left; | |
readonly TRight right; | |
readonly bool isRight; | |
Either(TLeft left, TRight right, bool isRight) { | |
this.left = left; | |
this.right = right; | |
this.isRight = isRight; | |
} | |
public bool IsLeft => !isRight; | |
public bool IsRight => isRight; | |
public TLeft Left { | |
get { | |
if (IsRight) throw new InvalidOperationException("No Left value present."); | |
return left; | |
} | |
} | |
public TRight Right { | |
get { | |
if (IsLeft) throw new InvalidOperationException("No Right value present."); | |
return right; | |
} | |
} | |
public static Either<TLeft, TRight> FromLeft(TLeft left) => new (left, default, false); | |
public static Either<TLeft, TRight> FromRight(TRight right) => new (default, right, true); | |
public TResult Match<TResult>(Func<TLeft, TResult> leftFunc, Func<TRight, TResult> rightFunc) { | |
return IsRight ? rightFunc(right) : leftFunc(left); | |
} | |
public Either<TLeft, TResult> Select<TResult>(Func<TRight, TResult> map) { | |
return IsRight ? Either<TLeft, TResult>.FromRight(map(right)) : Either<TLeft, TResult>.FromLeft(left); | |
} | |
public Either<TLeft, TResult> SelectMany<TResult>(Func<TRight, Either<TLeft, TResult>> bind) { | |
return IsRight ? bind(right) : Either<TLeft, TResult>.FromLeft(left); | |
} | |
public static implicit operator Either<TLeft, TRight>(TLeft left) => FromLeft(left); | |
public static implicit operator Either<TLeft, TRight>(TRight right) => FromRight(right); | |
public override string ToString() => IsRight ? $"Right({right})" : $"Left({left})"; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
public struct Optional<T> { | |
public static readonly Optional<T> NoValue = new Optional<T>(); | |
readonly bool hasValue; | |
readonly T value; | |
public Optional(T value) { | |
this.value = value; | |
hasValue = true; | |
} | |
public T Value => hasValue ? value : throw new InvalidOperationException("No value"); | |
public bool HasValue => hasValue; | |
public T GetValueOrDefault() => value; | |
public T GetValueOrDefault(T defaultValue) => hasValue ? value : defaultValue; | |
public TResult Match<TResult>(Func<T, TResult> onValue, Func<TResult> onNoValue) { | |
return hasValue ? onValue(value) : onNoValue(); | |
} | |
public Optional<TResult> SelectMany<TResult>(Func<T, Optional<TResult>> bind) { | |
return hasValue ? bind(value) : Optional<TResult>.NoValue; | |
} | |
public Optional<TResult> Select<TResult>(Func<T, TResult> map) { | |
return hasValue ? new Optional<TResult>(map(value)) : Optional<TResult>.NoValue; | |
} | |
public static Optional<TResult> Combine<T1, T2, TResult>(Optional<T1> first, Optional<T2> second, Func<T1, T2, TResult> combiner) { | |
if (first.HasValue && second.HasValue) { | |
return new Optional<TResult>(combiner(first.Value, second.Value)); | |
} | |
return Optional<TResult>.NoValue; | |
} | |
public static Optional<T> Some(T value) => new Optional<T>(value); | |
public static Optional<T> None() => NoValue; | |
public override bool Equals(object obj) => obj is Optional<T> other && Equals(other); | |
public bool Equals(Optional<T> other) => !hasValue ? !other.hasValue : EqualityComparer<T>.Default.Equals(value, other.value); | |
public override int GetHashCode() => (hasValue.GetHashCode() * 397) ^ EqualityComparer<T>.Default.GetHashCode(value); | |
public override string ToString() => hasValue ? $"Some({value})" : "None"; | |
public static implicit operator Optional<T>(T value) => new Optional<T>(value); | |
public static implicit operator bool(Optional<T> value) => value.hasValue; | |
public static explicit operator T(Optional<T> value) => value.Value; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment