# Result Monad The result monad is a [[Monad]] whose generic type is a union of a success case and a failure case. See [[Railway Oriented Programming]] for using the result monad for encoding domain errors. The result type usually comes with a `mapError` operator, making the result monad a [[Bifunctor]] as well. ## Result Monad in C# The `AsyncResult` is a collection of extension methods that allow a `Task<Result<>>` to be used directly as a monad. I've chosen to go this route because implementing an actual `AsyncResult` wrapper is incredibly difficult if you want it to be awaitable and also returnable from an `async` method (see [[Making an Object Awaitable]] and [[Making a Class a Valid Return From an async Method]]). Upon further consideration, I don't see any benefits for this in C#. The code below is easy to use even without a wrapper which I feel would only get in the way. ```csharp using System; using System.Threading.Tasks; public record Result<T, TError> { private record SuccessCase(T SuccessData) : Result<T, TError>; private record ErrorCase(TError ErrorData) : Result<T, TError>; public static Result<T, TError> Success(T value) => new SuccessCase(value); public static Result<T, TError> Error(TError errorData) => new ErrorCase(errorData); public static implicit operator Result<T, TError>(T v) => Success(v); private Result() { } public bool IsSuccess => this is SuccessCase; public TResult Either<TResult>( Func<T, TResult> onSuccess, Func<TError, TResult> onError) => this switch { SuccessCase successCase => onSuccess(successCase.SuccessData), ErrorCase errorCase => onError(errorCase.ErrorData), _ => throw new ArgumentOutOfRangeException() }; public T Force() => Either(x => x, _ => throw new("Forced result out of an Error")); } public static class Result { public static Result<T2, TError> Bind<T1, T2, TError>( this Result<T1, TError> m, Func<T1, Result<T2, TError>> f) => m.Either(f, Result<T2, TError>.Error); public static Result<T2, TError> Map<T1, T2, TError>( this Result<T1, TError> m, Func<T1, T2> map) => m.Bind<T1, T2, TError>(v => map(v)); public static Result<T, TError2> MapError<T, TError1, TError2>( this Result<T, TError1> m, Func<TError1, TError2> map) => m.Either( v => v, e => Result<T, TError2>.Error(map(e))); public static Result<IEnumerable<T2>, TError> Traverse<T1, T2, TError>( IEnumerable<T1> source, Func<T1, Result<T2, TError>> f) { var results = new List<T2>(); foreach (var element in source) { var result = f(element).Either<Result<IEnumerable<T2>, TError>?>( x => { results.Add(x); return null; }, Result<IEnumerable<T2>, TError>.Error); if (result != null) { return result; } } return results; } public static Result<IEnumerable<T>, TError> Sequence<T, TError>( this IEnumerable<Result<T, TError>> source) => Traverse(source, x => x); public static IEnumerable<T1> Choose<T, TError, T1>( this IEnumerable<Result<T, TError>> source, Func<T, T1> chooser) => source .Select(element => element.Either<T1?>(chooser, _ => default)) .Where(value => value != null)!; public static IEnumerable<T> Choose<T, TError>( this IEnumerable<Result<T, TError>> source) => Choose(source, x => x); public static T Get<T>(this Result<T, T> m) => m.Either(x => x, x => x); public static T DefaultWith<T, TError>( this Result<T, TError> m, Func<TError, T> defaultWith) => m.MapError(defaultWith).Get(); public static T DefaultValue<T, TError>( this Result<T, TError> m, T defaultValue) => m.DefaultWith(_ => defaultValue); public static Result<T2, TError> Select<T1, T2, TError>( this Result<T1, TError> m, Func<T1, T2> map) => m.Map(map); public static Result<T3, TError> SelectMany<T1, T2, T3, TError>( this Result<T1, TError> m, Func<T1, Result<T2, TError>> f, Func<T1, T2, T3> mapper) => m.Bind(t1 => f(t1).Map(t2 => mapper(t1, t2))); } public static class AsyncResult { public static async Task<Result<T2, TError>> Bind<T1, T2, TError>( this Task<Result<T1, TError>> m, Func<T1, Task<Result<T2, TError>>> f) { var r1 = await m; var r2 = await r1.Either( f, e => Task.FromResult(Result<T2, TError>.Error(e))); return r2; } public static Task<Result<T2, TError>> Map<T1, T2, TError>( this Task<Result<T1, TError>> m, Func<T1, T2> map) => m.Bind( v => Task.FromResult<Result<T2, TError>>(map(v))); public static async Task<Result<T, TError2>> MapError<T, TError1, TError2>( this Task<Result<T, TError1>> m, Func<TError1, TError2> map) { var r1 = await m; var r2 = r1.MapError(map); return r2; } public static async Task<T> Get<T>(this Task<Result<T, T>> m) { var r = await m; return r.Get(); } public static Task<T> DefaultWith<T, TError>( this Task<Result<T, TError>> m, Func<TError, T> defaultWith) => m.MapError(defaultWith).Get(); public static Task<T> DefaultValue<T, TError>( this Task<Result<T, TError>> m, T defaultValue) => m.DefaultWith(_ => defaultValue); public static Task<Result<T2, TError>> Select<T1, T2, TError>( this Task<Result<T1, TError>> m, Func<T1, T2> map) => m.Map(map); public static Task<Result<T3, TError>> SelectMany<T1, T2, T3, TError>( this Task<Result<T1, TError>> m, Func<T1, Task<Result<T2, TError>>> f, Func<T1, T2, T3> mapper) => m.Bind(t1 => f(t1).Map(t2 => mapper(t1, t2))); } ```