# 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)));
}
```