Skip to content

Instantly share code, notes, and snippets.

@palladin
Last active September 25, 2021 17:42
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save palladin/e81c9c837e0310008572de08cc885871 to your computer and use it in GitHub Desktop.
Save palladin/e81c9c837e0310008572de08cc885871 to your computer and use it in GitHub Desktop.
Maybe monad
// 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