Skip to content

Instantly share code, notes, and snippets.

@yapaxi
Created November 15, 2021 10:45
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 yapaxi/acb522fa81492099851df2cb816a7d6e to your computer and use it in GitHub Desktop.
Save yapaxi/acb522fa81492099851df2cb816a7d6e to your computer and use it in GitHub Desktop.
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