Last active
September 25, 2021 17:42
-
-
Save palladin/e81c9c837e0310008572de08cc885871 to your computer and use it in GitHub Desktop.
Maybe monad
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
// https://blog.neteril.org/blog/2017/04/26/maybe-computation-expression-csharp/ | |
[AsyncMethodBuilder(typeof(MaybeAsyncMethodBuilder<>))] | |
interface Option<T> { } | |
// Could use the closed type hierarchy Roslyn feature | |
// to be an approximation of a discriminated union | |
// https://github.com/dotnet/csharplang/issues/485 | |
sealed class None<T> : Option<T> { public static readonly None<T> Value = new None<T>(); } | |
sealed class Some<T> : Option<T> | |
{ | |
public readonly T Item; | |
public Some(T item) => Item = item; | |
public static explicit operator T(Some<T> maybe) => maybe.Item; | |
} | |
static class Some | |
{ | |
public static Some<T> Of<T>(T value) => new Some<T>(value); | |
} | |
class MaybeAsyncMethodBuilder<T> | |
{ | |
Option<T> result = None<T>.Value; | |
public static MaybeAsyncMethodBuilder<T> Create() => new MaybeAsyncMethodBuilder<T>(); | |
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine | |
{ | |
// Simply start the state machine which will execute our code | |
stateMachine.MoveNext(); | |
} | |
public Option<T> Task => result; | |
public void SetResult(T result) => this.result = Some.Of(result); | |
public void SetException(Exception ex) { /* We leave the result to None */ } | |
// Unused methods | |
public void SetStateMachine(IAsyncStateMachine stateMachine) { } | |
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) | |
where TAwaiter : IMaybeAwaiter | |
where TStateMachine : IAsyncStateMachine | |
{ | |
} | |
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) | |
where TAwaiter : IMaybeAwaiter | |
where TStateMachine : IAsyncStateMachine | |
{ | |
} | |
} | |
interface IMaybeAwaiter : ICriticalNotifyCompletion | |
{ } | |
class MaybeAwaiter<T> : IMaybeAwaiter | |
{ | |
Option<T> maybe; | |
public MaybeAwaiter(Option<T> maybe) => this.maybe = maybe; | |
public bool IsCompleted => maybe is Some<T>; | |
public void OnCompleted(Action continuation) | |
{ | |
/* We never need to execute the continuation cause | |
* we only reach here when the result is None which | |
* means we are trying to short-circuit everything | |
* else | |
*/ | |
} | |
public void UnsafeOnCompleted(Action continuation) | |
{ | |
} | |
public T GetResult() => ((Some<T>)maybe).Item; | |
} | |
static class OptionExtensions | |
{ | |
public static MaybeAwaiter<T> GetAwaiter<T>(this Option<T> maybe) => new MaybeAwaiter<T>(maybe); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment