Podemos crear nuestra propia clase monádica, similar a std::expected o Result de Rust:
public abstract class Result<T>
{
public abstract Result<U> Map<U>(Func<T, U> f);
public abstract Result<U> FlatMap<U>(Func<T, Result<U>> f);
public abstract T OrElse(T fallback);
public static Result<T> Ok(T value) => new OkResult<T>(value);
public static Result<T> Error(string message) => new ErrorResult<T>(message);
}
public class OkResult<T> : Result<T>
{
private readonly T value;
public OkResult(T v) => value = v;
public override Result<U> Map<U>(Func<T, U> f) => Result<U>.Ok(f(value));
public override Result<U> FlatMap<U>(Func<T, Result<U>> f) => f(value);
public override T OrElse(T fallback) => value;
}
public class ErrorResult<T> : Result<T>
{
private readonly string message;
public ErrorResult(string msg) => message = msg;
public override Result<U> Map<U>(Func<T, U> f) => Result<U>.Error(message);
public override Result<U> FlatMap<U>(Func<T, Result<U>> f) => Result<U>.Error(message);
public override T OrElse(T fallback) => fallback;
}
Uso:
Result<int> parse(string s) => int.TryParse(s, out var n) ? Result<int>.Ok(n) : Result<int>.Error("Invalid");
var r = parse("42")
.FlatMap(x => Result<int>.Ok(x * 2))
.Map(x => x + 1)
.OrElse(0);
Console.WriteLine(r); // 85
Los métodos monádicos permiten escribir código más expresivo y seguro en C#, eliminando condicionales repetitivos y manejo manual de errores.
