Skip to content

Instantly share code, notes, and snippets.

@dzmitry-lahoda
Last active June 2, 2020 09:26
Show Gist options
  • Save dzmitry-lahoda/78bd89c3aa804aa0467dcba0a2e1defd to your computer and use it in GitHub Desktop.
Save dzmitry-lahoda/78bd89c3aa804aa0467dcba0a2e1defd to your computer and use it in GitHub Desktop.
#nullable enable
using System;
using System.Fun;
using System.Net;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Mvc
{
public interface MvcError : IResult
{
bool IsOk => false;
bool IsError => true;
ProblemDetails Value { get; }
}
/// <summary>
/// Return result for web.
/// </summary>
public abstract class MvcResult<T>
where T : notnull
{
public static MvcResult<T> Of(ProblemDetails value) => new Error(value);
public static MvcResult<T> Of(T value) => new Ok(value);
public abstract bool IsOk { get; }
public abstract bool IsError { get; }
public static implicit operator MvcResult<T>(T value) => new Ok(value);
public static implicit operator MvcResult<T>(ProblemDetails value) => new Error(value);
private MvcResult() { }
public sealed class Ok : MvcResult<T>, Ok<T>
{
public Ok(T value) => Value = value ?? throw new ArgumentNullException("value");
public T Value { get; }
public static implicit operator Ok(T value) => new Ok(value);
public static implicit operator T(Ok result) => result.Value;
public override bool IsOk => true;
public override bool IsError => false;
}
public sealed class Error : MvcResult<T>, MvcError
{
public Error(ProblemDetails value) => Value = value ?? throw new ArgumentNullException("value");
public ProblemDetails Value { get; }
public static implicit operator Error(ProblemDetails value) => new Error(value);
public static implicit operator ProblemDetails(Error result) => result.Value;
public override bool IsOk => false;
public override bool IsError => true;
public ObjectResult Problem() =>
new ObjectResult(Value) { StatusCode = Value.Status };
}
public ObjectResult Result =>
(this) switch
{
Ok o => new ObjectResult(o),
Error e => e.Problem()
};
public MvcResult<TOut> Switch<TOut>(Func<T, TOut> okCase, Func<ProblemDetails, ProblemDetails> errorCase) =>
this switch
{
Ok ok => okCase(ok.Value),
Error fail => errorCase(fail.Value)
};
public MvcResult<TOut> OkCase<TOut>(Func<T, TOut> okCase) =>
this switch
{
Ok ok => okCase(ok.Value),
Error fail => fail.Value
};
public async Task<MvcResult<TOut>> SwitchAsync<TOut>(Func<T, Task<TOut>> okCase, Func<ProblemDetails, ProblemDetails> errorCase) =>
this switch
{
Ok ok => await okCase(ok.Value),
Error fail => errorCase(fail.Value)
};
public async Task<MvcResult<TOut>> OkCaseAsync<TOut>(Func<T, Task<TOut>> okCase) =>
this switch
{
Ok ok => await okCase(ok.Value),
Error fail => fail.Value
};
}
}
using System;
using System.Collections.Generic;
using System.Fun;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Mvc
{
public static class ControllerExtensions
{
public static ObjectResult Problem(this ControllerBase self, ProblemDetails data) =>
self.Problem(data);
// of result
public static MvcResult<TOut> Switch<T, TOut>(this Result<T> self, Func<T, TOut> okCase, Func<Exception, ProblemDetails> errorCase) =>
self switch
{
Result<T>.Ok ok => okCase(ok.Value),
Result<T>.Error fail => errorCase(fail.Value)
};
// to result
public static Result<TOut> Switch<T, TOut>(this MvcResult<T> self, Func<T, TOut> okCase, Func<ProblemDetails, Exception> errorCase) =>
self switch
{
MvcResult<T>.Ok ok => okCase(ok.Value),
MvcResult<T>.Error fail => errorCase(fail.Value)
};
}
}
// Choose Optional(Opt) and write combinators for Opt<T, Exception> Opt<T,Task<TOther>, Opt<T, ProblemDetails>
// Unions are good per ce on C#8 recursive pattern matching and will be best on 9 with generator and #r nuget
// Does not go into forest of Free Monads, Tagles Final, Interpreters
//
// Dictionary<string, Foo> stats =
// await bar.SelectMany(x => x.Bazz)
// .Distinct()
// .Batch(13)
// .SelectTo(fizz, workWork)
// .SelectManyAsync(x => x)
// .ToDictionaryAsync(x => x.A, _ => _);
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace System.Linq
{
internal static class Extensions
{
public static Func<T2, TResult> Partial<T1, T2, TResult>(T1 first, Func<T1, T2, TResult> map)
{
Func<T2, TResult> func = t2 => map(first, t2);
return func;
}
/// <summary>
/// Forward pipe operator (`|>` in F#) but with side effect propagating the original `x` value
/// </summary>
public static T Tap<T>(this T x, Action<T> effect)
{
effect(x);
return x;
}
/// <summary>
/// Forward pipe operator (`|>` in F#) but with side effect propagating the original `x` value and the state object
/// </summary>
public static T Tap<T, S>(this T x, S state, Action<T, S> effect)
{
effect(x, state);
return x;
}
public static IEnumerable<TResult> SelectTo<T1, T2, TResult>(this IEnumerable<T2> self, T1 first, Func<T1, T2, TResult> selector) =>
self.Select(_ => _.To(first, selector));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult To<T1, T2, T3, TResult>(this (T2 second, T3 third) self, T1 first, Func<T1, T2, T3, TResult> map) =>
map(first, self.second, self.third);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult To<T1, T2, T3, TResult>(this T3 self, T1 first, T2 second, Func<T1, T2, T3, TResult> map) =>
map(first, second, self);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult To<T1, T2, T3, TResult>(this T3 self, (T1 first, T2 second) part, Func<T1, T2, T3, TResult> map) =>
map(part.first, part.second, self);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult To<T1, T2, T3, TResult>(this T3 self, (T1 first, T2 second) part, Func<(T1, T2), T3, TResult> map) =>
map(part, self);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult To<T1, T2, T3, T4, TResult>(this T4 self, T1 first, T2 second, T3 third, Func<T1, T2, T3, T4, TResult> map) =>
map(first, second, third, self);
/// <summary>
/// Pipe operator for funcitonal chaining.
/// </summary>
public static R To<T, R>(this T self, Func<T, R> map) => map(self);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult To<T1, T2, TResult>(this T2 self, T1 first, Func<T1, T2, TResult> map) =>
map(first, self);
// similar to `as..select` in Gremlin or to `let` in C# LINQ
public static IEnumerable<T> SelectLet<T, V>(this IEnumerable<T> self, out V var, Func<IEnumerable<T>, V> sideEffect)
{
var = self.To(sideEffect);
return self;
}
public static IEnumerable<T> SelectLet<T>(this IEnumerable<T> self, out IEnumerable<T> var)
{
var = self;
return self;
}
///<summary>
/// If null, than throws.
/// If not null than passes object as it as not nullable.
///</summary>
public static T NotNull<T>(this T self, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "")
where T : class
=>
self != null ? self : throw new ArgumentNullException(memberName);
public static IEnumerable<T1> SelectFirst<T1, T2>(this IEnumerable<ValueTuple<T1, T2>> self) =>
self.Select(x => x.Item1);
}
}
#nullable enable
using System.Threading.Tasks;
namespace System.Fun
{
public interface IResult
{
bool IsOk { get; }
bool IsError { get; }
}
public interface IOk : IResult
{
bool IsOk => true;
bool IsError => false;
}
public interface Ok<T> : IOk
{
T Value { get; }
}
public interface Error : IResult
{
bool IsOk => false;
bool IsError => true;
Exception Value { get; }
}
/// <summary>
/// Optional result. Must check for null.
/// This result works with C# 8 pattern matching.
/// </summary>
public abstract class OptionalResult<T> : IResult
where T : class
{
public static OptionalResult<T?> Of(Exception value) => new Error(value);
public static OptionalResult<T?> Of(T? value) => new Ok(value);
public abstract bool IsOk { get; }
public abstract bool IsError { get; }
private OptionalResult() { }
public static implicit operator OptionalResult<T>(T value) => new Ok(value);
public static implicit operator OptionalResult<T>(Exception value) => new Error(value);
public sealed class Ok : OptionalResult<T?>, Ok<T?>
{
public Ok(T? value) => Value = value;
public T? Value { get; }
public static implicit operator Ok(T value) => new Ok(value);
public static implicit operator T(Ok result) => result.Value;
public override bool IsOk => true;
public override bool IsError => false;
}
public sealed class Error : OptionalResult<T>, Fun.Error
{
public Error(Exception value) => Value = value ?? throw new ArgumentNullException("value");
public Exception Value { get; }
public static implicit operator Error(Exception value) => new Error(value);
public static implicit operator Exception(Error result) => result.Value;
public override bool IsOk => false;
public override bool IsError => true;
public Exception Throw() => throw Value;
}
}
/// <summary>
/// Non optional result.
/// This result works with C# 8 pattern matching.
/// </summary>
public abstract class Result<T> : IResult
where T : notnull
{
public static Result<T> Of(Exception value) => new Error(value);
public static Result<T> Of(T value) => new Ok(value);
public abstract bool IsOk { get; }
public abstract bool IsError { get; }
private Result() { }
public static implicit operator Result<T>(T value) => new Ok(value);
public static implicit operator Result<T>(Exception value) => new Error(value);
public sealed class Ok : Result<T>, Ok<T>
{
public Ok(T value) => Value = value ?? throw new ArgumentNullException("value");
public T Value { get; }
public static implicit operator Ok(T value) => new Ok(value);
public static implicit operator T(Ok result) => result.Value;
public override bool IsOk => true;
public override bool IsError => false;
}
public sealed class Error : Result<T>, Fun.Error
{
public Error(Exception value) => Value = value ?? throw new ArgumentNullException("value");
public Exception Value { get; }
public static implicit operator Error(Exception value) => new Error(value);
public static implicit operator Exception(Error result) => result.Value;
public override bool IsOk => false;
public override bool IsError => true;
public Exception Throw() => throw Value;
}
public Result<TOut> Switch<TOut>(Func<T, TOut> okCase, Func<Exception, Exception> errorCase) =>
this switch
{
Ok ok => okCase(ok.Value),
Error fail => errorCase(fail.Value)
};
public Result<TOut> OkCase<TOut>(Func<T, TOut> okCase) =>
this switch
{
Ok ok => okCase(ok.Value),
Error fail => fail.Value
};
public async Task<Result<TOut>> SwitchAsync<TOut>(Func<T, Task<TOut>> okCase, Func<Exception, Exception> errorCase) =>
this switch
{
Ok ok => await okCase(ok.Value),
Error fail => errorCase(fail.Value)
};
public async Task<Result<TOut>> OkCaseAsync<TOut>(Func<T, Task<TOut>> okCase) =>
this switch
{
Ok ok => await okCase(ok.Value),
Error fail => fail.Value
};
}
}
using System.Collections.Generic;
using System.Fun;
using System.Runtime.ExceptionServices;
namespace System.Linq
{
public static class ResultExtensions
{
public static IEnumerable<Result<TResult>> TrySelect<TIn, TResult>(this IEnumerable<TIn> self, Func<TIn, TResult> selector)
{
foreach (var item in self)
{
Result<TResult> result;
try
{
result = selector(item);
}
catch (Exception ex)
{
result = ExceptionDispatchInfo.Capture(ex).SourceException;
}
yield return result;
}
}
public static IEnumerable<TResult> SelectOk<TResult>(this IEnumerable<Result<TResult>> self) =>
self.Where(x => x.IsOk).Cast<Result<TResult>.Ok>().Select(x=> x.Value);
public static void SelectThrow<TResult>(this IEnumerable<Result<TResult>> self)
{
var errors = self.Where(x => x.IsError).Cast<Result<TResult>.Error>().Select(x => x.Value);
if (errors.Any())
throw new AggregateException(errors);
}
}
}
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
namespace System.Threading.Tasks
{
internal static class Extensions
{
public static async Task<TResult> SelectAsync<T, TResult>(this Task<T> self, Func<T, TResult> map) =>
map(await self.IgnoreContext());
public static async Task<TResult> ToAsync<T, TResult>(this Task<T> self, Func<T, Task<TResult>> map) =>
await map(await self.IgnoreContext()).IgnoreContext();
public static async Task<TResult> ToAsync<T, TResult>(this Task<T> self, Func<T, TResult> map) =>
map(await self.IgnoreContext());
public static async Task<TResult> To<T, TResult>(this Task<T> self, Func<T, TResult> map)
{
var result = await self;
return map(result);
}
public static async ValueTask<TResult> SelectAsync<T, TResult>(this ValueTask<T> x, Func<T, TResult> map) =>
map(await x.IgnoreContext());
/// <summary>
/// Returns completed task if current is null.
/// </summary>
public static Task IfNullCompleted(this Task self) => self ?? Task.CompletedTask;
public static async ValueTask<TResult> ToAsync<T, TResult>(this ValueTask<T> x, Func<T, Task<TResult>> map) =>
await map(await x.IgnoreContext()).IgnoreContext();
public static async Task ToAsync<T>(this Task<T> x, Func<T, Task> map) =>
await map(await x.IgnoreContext()).IgnoreContext();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ConfiguredTaskAwaitable<T> IgnoreContext<T>(this Task<T> task) =>
task.ConfigureAwait(false);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ConfiguredTaskAwaitable IgnoreContext(this Task task) =>
task.ConfigureAwait(false);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ConfiguredValueTaskAwaitable<T> IgnoreContext<T>(this ValueTask<T> task) =>
task.ConfigureAwait(false);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ConfiguredValueTaskAwaitable IgnoreContext(this ValueTask task) =>
task.ConfigureAwait(false);
// we do not have type classes, so just produce combination for each container(List/Enumerable or Task/Value task for our cases)
// once written - gets reused
public static async Task<IEnumerable<TResult>> SelectAsync<TOut, TResult>(this Task<List<TOut>> self, Func<TOut, TResult> selector)
{
var items = await self;
return items.Select(selector);
}
public static Task<IEnumerable<TResult>> SelectToAsync<T1, T2, TResult>(this Task<IEnumerable<T2>> self, T1 first, Func<T1, T2, TResult> selector) =>
self.SelectAsync(_ => _.To(first, selector));
public static Task<IEnumerable<TResult>> SelectToAsync<T1, T2, TResult>(this Task<List<T2>> self, T1 first, Func<T1, T2, TResult> selector) =>
self.SelectAsync(_ => _.To(first, selector));
public static Task<IEnumerable<TResult>> SelectToAsync<T1, T2, T3, TResult>(this Task<List<T3>> self, T1 first, T2 second, Func<T1, T2, T3, TResult> selector) =>
self.SelectAsync(_ => _.To(first, second, selector));
public static Task<IEnumerable<TResult>> SelectToAsync<T1, T2, T3, TResult>(this Task<IEnumerable<T3>> self, T1 first, T2 second, Func<T1, T2, T3, TResult> selector) =>
self.SelectAsync(_ => _.To(first, second, selector));
// C# 9 allows to generate these repated items
public static async Task<IEnumerable<TResult>> SelectAsync<TOut, TResult>(this Task<HashSet<TOut>> self, Func<TOut, TResult> selector)
{
var items = await self;
return items.Select(selector);
}
public static async Task<IEnumerable<TResult>> SelectAsync<TOut, TResult>(this Task<TOut[]> self, Func<TOut, TResult> selector)
{
var items = await self;
return items.Select(selector);
}
public static async Task<IEnumerable<TResult>> SelectAsync<TOut, TResult>(this Task<IList<TOut>> self, Func<TOut, TResult> selector)
{
var items = await self;
return items.Select(selector);
}
public static async Task<IEnumerable<TResult>> SelectAsync<TOut, TResult>(this Task<IEnumerable<TOut>> self, Func<TOut, TResult> selector)
{
var items = await self;
return items.Select(selector);
}
public static async ValueTask<IEnumerable<TResult>> SelectAsync<TOut, TResult>(this ValueTask<IEnumerable<TOut>> self, Func<TOut, TResult> selector)
{
var items = await self;
return items.Select(selector);
}
public static async Task<IEnumerable<TResult>> SelectManyAsync<T, TResult>(this IEnumerable<Task<T>> self, Func<T, IEnumerable<TResult>> map)
{
var results = new List<TResult>();
foreach(var item in self)
{
var result = await item;
results.AddRange(map(result));
}
return results;
}
public static async Task<Dictionary<TKey,TValue>> ToDictionaryAsync<T, TKey, TValue>(this Task<IEnumerable<T>> self, Func<T, TKey> key, Func<T, TValue> value)
{
var result = await self;
return result.ToDictionary(key, value);
}
public static async ValueTask<IEnumerable<TResult>> SelectManyAsync<T, TResult>(this IEnumerable<ValueTask<T>> self, Func<T, IEnumerable<TResult>> map)
{
var results = new List<TResult>();
foreach (var item in self)
{
var result = await item;
results.AddRange(map(result));
}
return results;
}
public static async ValueTask<Dictionary<TKey, TValue>> ToDictionaryAsync<T, TKey, TValue>(this ValueTask<IEnumerable<T>> self, Func<T, TKey> key, Func<T, TValue> value)
{
var result = await self;
return result.ToDictionary(key, value);
}
public static async Task<HashSet<T>> ToHashSetAsync<T>(this Task<IEnumerable<T>> self)
{
var result = await self;
return result.ToHashSet();
}
// etc...
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment