Skip to content

Instantly share code, notes, and snippets.

@marcinnajder
Last active May 20, 2022 16:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marcinnajder/9458a0532873837c70188ec972b97465 to your computer and use it in GitHub Desktop.
Save marcinnajder/9458a0532873837c70188ec972b97465 to your computer and use it in GitHub Desktop.
async Optional<string> ProcessText(string text1, string text2)
{
int number1 = await TryParseInt(text1);
int number2 = await TryParseInt(text2);
return (number1 + number2).ToString();
}
Optional<int> TryParseInt(string text) =>
int.TryParse(text, out var result) ? new Optional<int>(result) : Optional<int>.None;
// --
public partial class Optional<T>
{
public bool HasValue { get; }
public T Value { get; }
public Optional() => (HasValue, Value) = (false, default);
public Optional(T value) => (HasValue, Value) = (true, value);
public static Optional<T> None { get; } = new Optional<T>();
}
// --
// copy code below for any monad type Task<T>, IEnumerable<T>, Nullable<T>, IObservable<T>, Array<T>, ...
// ... and implement functions Return, Select, SelectMany
[AsyncMethodBuilder(typeof(OptionalMethodBuilder<>))]
public partial class Optional<T> { }
public static class OptionalExtensions
{
public static OptionalAwaiter<T> GetAwaiter<T>(this Optional<T> monad) => new OptionalAwaiter<T>(monad);
}
internal interface IOptionalAwaiter
{
Optional<object> MoveNext(Action moveNext, Func<Optional<object>> getNextMonad);
}
public class OptionalAwaiter<T> : INotifyCompletion, IOptionalAwaiter
{
public bool IsCompleted => false;
public void OnCompleted(Action continuation) { }
protected T result = default;
public T GetResult() => result;
private Optional<T> monad;
public OptionalAwaiter(Optional<T> monad) => this.monad = monad;
Optional<object> IOptionalAwaiter.MoveNext(Action moveNext, Func<Optional<object>> getNextMonad)
{
return monad.SelectMany<T, object>(t =>
{
result = t;
moveNext();
return getNextMonad();
});
}
}
public class OptionalMethodBuilder<T>
{
public void Start<TSM>(ref TSM stateMachine) where TSM : IAsyncStateMachine => stateMachine.MoveNext();
public void SetStateMachine(IAsyncStateMachine stateMachine) { }
public void SetException(Exception exception) { }
public void AwaitUnsafeOnCompleted<TA, TSM>(ref TA awaiter, ref TSM stateMachine)
where TA : ICriticalNotifyCompletion where TSM : IAsyncStateMachine
{ }
private bool isFirstCall = true;
private Optional<object> currentTask;
public Optional<T> Task { get; private set; }
public static OptionalMethodBuilder<T> Create() => new OptionalMethodBuilder<T>();
public void AwaitOnCompleted<TA, TSM>(ref TA awaiter, ref TSM stateMachine)
where TA : INotifyCompletion where TSM : IAsyncStateMachine
{
if (awaiter is IOptionalAwaiter optionalAwaiter)
{
var wasFirstCall = isFirstCall;
isFirstCall = false;
currentTask = optionalAwaiter.MoveNext(stateMachine.MoveNext, () => currentTask);
if (wasFirstCall)
{
Task = currentTask.Select(v => (T)v);
}
}
}
public void SetResult(T result)
{
if (isFirstCall) { Task = Monad.ReturnO(result); }
else { currentTask = Monad.ReturnO<object>(result); }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment