Last active
May 20, 2022 16:26
-
-
Save marcinnajder/9458a0532873837c70188ec972b97465 to your computer and use it in GitHub Desktop.
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
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