Created
November 15, 2021 10:45
-
-
Save yapaxi/acb522fa81492099851df2cb816a7d6e to your computer and use it in GitHub Desktop.
This file contains hidden or 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; | |
using System.Linq; | |
using System.Runtime.CompilerServices; | |
using System.Threading; | |
using System.Threading.Tasks; | |
namespace ConsoleApp50 | |
{ | |
class Program | |
{ | |
static async Task Main(string[] args) | |
{ | |
var r = await ( | |
from q in GetA() | |
from b in GetB() | |
from _ in Log(q, b) | |
from c in GetC().MapError(z => new Option<short>.Error($"Modified error; q: '{q}', b: '{b}', original-error:'{z.Message}'")) | |
select q > 0 ? BranchA(q) : BranchB(b + c) | |
); | |
Console.WriteLine(r); | |
} | |
static async Task<Option<int>> GetA() => 1; | |
static async Task<Option<long>> GetB() => 2; | |
static async Task<Option<short>> GetC() => new Option<short>.Error("Some error"); | |
static async Task<Option<long>> BranchA(long x) => x; | |
static async Task<Option<long>> BranchB(long x) => x; | |
static async Task<Option<int>> Log(params object[] p) | |
{ | |
foreach (var v in p) | |
{ | |
Console.WriteLine(v); | |
} | |
return new Option<int>.Some(0); | |
} | |
} | |
public static class TaskExtensions | |
{ | |
public async static Task<Option<A>> MapError<A>(this Task<Option<A>> source, Func<Option<A>.Error, Option<A>.Error> selector) | |
{ | |
return await source switch | |
{ | |
Option<A>.Some s => s.Val, | |
Option<A>.None s => s, | |
Option<A>.Error s => selector(s), | |
_ => throw new InvalidOperationException() | |
}; | |
} | |
public async static Task<Option<B>> Select<A, B>(this Task<Option<A>> source, Func<A, Task<Option<B>>> selector) | |
{ | |
return await source switch | |
{ | |
Option<A>.Some s => await selector(s.Val), | |
Option<A>.None _ => Option<B>.None.OptInstance, | |
Option<A>.Error s => s.Cast<B>(), | |
_ => throw new InvalidOperationException() | |
}; | |
} | |
public static async Task<Option<C>> SelectMany<A, B, C>(this Task<Option<A>> source, Func<A, Task<Option<B>>> f1, Func<A, B, C> f2) | |
{ | |
return await source switch | |
{ | |
Option<A>.Some s => await f1(s.Val) switch | |
{ | |
Option<B>.Some s1 => f2(s.Val, s1.Val), | |
Option<B>.None _ => Option<C>.None.OptInstance, | |
Option<B>.Error s2 => s2.Cast<C>(), | |
_ => throw new InvalidOperationException() | |
}, | |
Option<A>.None _ => Option<C>.None.OptInstance, | |
Option<A>.Error s => s.Cast<C>(), | |
_ => throw new InvalidOperationException() | |
}; | |
} | |
public static async Task<Option<C>> SelectMany<A, B, C>(this Task<Option<A>> source, Func<A, Task<Option<B>>> f1, Func<A, B, Option<C>> f2) | |
{ | |
return await source switch | |
{ | |
Option<A>.Some s => await f1(s.Val) switch | |
{ | |
Option<B>.Some s1 => f2(s.Val, s1.Val), | |
Option<B>.None _ => Option<C>.None.OptInstance, | |
Option<B>.Error s2 => s2.Cast<C>(), | |
_ => throw new InvalidOperationException() | |
}, | |
Option<A>.None _ => Option<C>.None.OptInstance, | |
Option<A>.Error s => s.Cast<C>(), | |
_ => throw new InvalidOperationException() | |
}; | |
} | |
public static async Task<Option<C>> SelectMany<A, B, C>(this Task<Option<A>> source, Func<A, Task<Option<B>>> f1, Func<A, B, Task<Option<C>>> f2) | |
{ | |
return await source switch | |
{ | |
Option<A>.Some s => await f1(s.Val) switch | |
{ | |
Option<B>.Some s1 => await f2(s.Val, s1.Val), | |
Option<B>.None _ => Option<C>.None.OptInstance, | |
Option<B>.Error s2 => s2.Cast<C>(), | |
_ => throw new InvalidOperationException() | |
}, | |
Option<A>.None _ => Option<C>.None.OptInstance, | |
Option<A>.Error s => s.Cast<C>(), | |
_ => throw new InvalidOperationException() | |
}; | |
} | |
} | |
public class Option<T> | |
{ | |
public static implicit operator Option<T>(T s) => new Some(s); | |
public class Some : Option<T> | |
{ | |
public static implicit operator T(Some s) => s.Val; | |
public static implicit operator Some(T s) => new Some(s); | |
public Some(T val) | |
{ | |
Val = val; | |
} | |
public T Val { get; } | |
public override string ToString() | |
{ | |
return $"Some<{typeof(T).Name}>({Val})"; | |
} | |
} | |
public class None : Option<T> | |
{ | |
public static readonly None Instance = new None(); | |
public static Option<T> OptInstance => Instance; | |
public None() | |
{ | |
} | |
public override string ToString() | |
{ | |
return $"None<{typeof(T).Name}>"; | |
} | |
} | |
public class Error : Option<T> | |
{ | |
public Error(string message) | |
{ | |
Message = message; | |
} | |
public string Message { get; } | |
public Option<T2>.Error Cast<T2>() => new(Message); | |
public override string ToString() | |
{ | |
return $"Error<{typeof(T).Name}>; {Message}"; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment