Last active
October 17, 2019 23:07
-
-
Save Pzixel/fd4ccfcf011770075d53775eee13d8d8 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
void Main() | |
{ | |
var v = from x in 10.AsResult() | |
from y in 0.AsResult() | |
select Result.Try (() => x/y); | |
v.Dump(); | |
} |
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
public interface IResult<T, E> | |
{ | |
} | |
public class Ok<T, E> : IResult<T, E> | |
{ | |
public T Value { get; } | |
public Ok(T value) | |
{ | |
Value = value; | |
} | |
} | |
public class Error<T, E> : IResult<T, E> | |
{ | |
public E Value { get; } | |
public Error(E value) | |
{ | |
Value = value; | |
} | |
} | |
public static class Result | |
{ | |
public static IResult<T, Exception> AsResult<T>(this T value) => new Ok<T, Exception>(value); | |
public static IResult<T, Exception> Try<T>(Func<T> func) | |
{ | |
try | |
{ | |
return new Ok<T, Exception>(func()); | |
} | |
catch (Exception ex) | |
{ | |
return new Error<T, Exception>(ex); | |
} | |
} | |
public static Task<IResult<T, Exception>> TryAsync<T>(Func<Task<T>> func) => | |
func().ContinueWith(x => Try(() => x.Result)); | |
public static IResult<TResult, E> Map<T, TResult, E>(this IResult<T, E> result, Func<T, TResult> map) | |
{ | |
switch (result) | |
{ | |
case Ok<T, E> ok: | |
return new Ok<TResult, E>(map(ok.Value)); | |
case Error<T, E> err: | |
return new Error<TResult, E>(err.Value); | |
default: | |
throw new InvalidOperationException($"Unknown result type {result.GetType().Name}"); | |
} | |
} | |
public static async Task<IResult<TResult, E>> MapAsync<T, TResult, E>(this IResult<T, E> result, Func<T, Task<TResult>> map) | |
{ | |
switch (result) | |
{ | |
case Ok<T, E> ok: | |
return new Ok<TResult, E>(await map(ok.Value)); | |
case Error<T, E> err: | |
return new Error<TResult, E>(err.Value); | |
default: | |
throw new InvalidOperationException($"Unknown result type {result.GetType().Name}"); | |
} | |
} | |
public static IResult<T, EResult> MapError<T, E, EResult>(this IResult<T, E> result, Func<E, EResult> map) | |
{ | |
switch (result) | |
{ | |
case Ok<T, E> ok: | |
return new Ok<T, EResult>(ok.Value); | |
case Error<T, E> err: | |
return new Error<T, EResult>(map(err.Value)); | |
default: | |
throw new InvalidOperationException($"Unknown result type {result.GetType().Name}"); | |
} | |
} | |
public static IResult<TResult, E> Bind<T, TResult, E>(this IResult<T, E> result, Func<T, IResult<TResult, E>> bind) | |
{ | |
switch (result) | |
{ | |
case Ok<T, E> ok: | |
return bind(ok.Value); | |
case Error<T, E> err: | |
return new Error<TResult, E>(err.Value); | |
default: | |
throw new InvalidOperationException($"Unknown result type {result.GetType().Name}"); | |
} | |
} | |
public static Task<IResult<TResult, E>> BindAsync<T, TResult, E>(this IResult<T, E> result, Func<T, Task<IResult<TResult, E>>> bind) | |
{ | |
switch (result) | |
{ | |
case Ok<T, E> ok: | |
return bind(ok.Value); | |
case Error<T, E> err: | |
return Task.FromResult((IResult<TResult, E>)new Error<TResult, E>(err.Value)); | |
default: | |
throw new InvalidOperationException($"Unknown result type {result.GetType().Name}"); | |
} | |
} | |
public static IResult<T, E> Scan<T, E>(this IResult<T, E> result, Action<T> onOk, Action<E> onError) | |
{ | |
switch (result) | |
{ | |
case Ok<T, E> ok: | |
onOk(ok.Value); | |
return result; | |
case Error<T, E> err: | |
onError(err.Value); | |
return result; | |
default: | |
throw new InvalidOperationException($"Unknown result type {result.GetType().Name}"); | |
} | |
} | |
} | |
public static class ResultLinq | |
{ | |
public static IResult<TResult, E> Select<T, E, TResult>(this IResult<T, E> value, Func<T, TResult> map) => value.Map(map); | |
public static IResult<TResult, E> SelectMany<T, E, TResult>(this IResult<T, E> value, Func<T, IResult<TResult, E>> bind) => value.Bind(bind); | |
public static IResult<TResult, E> SelectMany<TA, TB, E, TResult>(this IResult<TA, E> value, Func<TA, IResult<TB, E>> bind, Func<TA, TB, TResult> project) | |
{ | |
switch (value) | |
{ | |
case Error<TA, E> err: | |
return new Error<TResult, E>(err.Value); | |
case Ok<TA, E> oka: | |
var b = bind(oka.Value); | |
switch (b) | |
{ | |
case Error<TB, E> err: | |
return new Error<TResult, E>(err.Value); | |
case Ok<TB, E> okb: | |
return new Ok<TResult, E>(project(oka.Value, okb.Value)); | |
default: | |
throw new InvalidOperationException($"Unknown result type {value.GetType().Name}"); | |
} | |
default: | |
throw new InvalidOperationException($"Unknown result type {value.GetType().Name}"); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment