Skip to content

Instantly share code, notes, and snippets.

@Porges
Created December 7, 2016 02:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Porges/144b55bd2027f141fcb691c3c103a570 to your computer and use it in GitHub Desktop.
Save Porges/144b55bd2027f141fcb691c3c103a570 to your computer and use it in GitHub Desktop.
using System;
namespace Result
{
class Program
{
static void Main(string[] args)
{
var r = new Random();
for (int i = 0; i < 10; ++i)
{
var ex1 =
from value in MaybeFails(r, "first")
from value2 in MaybeFails(r, "second")
select value + value2;
Console.WriteLine(ex1);
// this exercises Select
//var ex2 =
// from value in MaybeFails(r, "only")
// let value1 = value + 1
// select value1;
}
}
// A method that might succeed or fail:
private static Result<string, int> MaybeFails(Random rand, string failureMessage)
{
if (rand.Next(3) == 2)
{
return failureMessage;
}
return 123;
}
}
public abstract class Result<TFail, TSucceed>
{
private Result() { } // only 2 instances
/// <summary>
/// Map the success argument.
/// </summary>
public abstract Result<TFail, TSucceed2> Map<TSucceed2>(Func<TSucceed, TSucceed2> projection);
/// <summary>
/// Pattern-match the result.
/// </summary>
public abstract T Match<T>(Func<TFail, T> onFailure, Func<TSucceed, T> onSuccess);
/// <summary>
/// Pattern-match the result with an action.
/// </summary>
public abstract void Match<T>(Action<TFail> onFailure, Action<TSucceed> onSuccess);
/// <summary>
/// Traditional .NET decomposition.
/// </summary>
public abstract bool TryGetValue(out TSucceed value);
private sealed class Fail : Result<TFail, TSucceed>
{
private readonly TFail _value;
public Fail(TFail value)
{
_value = value;
}
public override T Match<T>(Func<TFail, T> onFailure, Func<TSucceed, T> onSuccess)
=> onFailure(_value);
public override void Match<T>(Action<TFail> onFailure, Action<TSucceed> onSuccess)
=> onFailure(_value);
public override bool TryGetValue(out TSucceed value)
{
value = default(TSucceed);
return false;
}
public override Result<TFail, TSucceed2> Map<TSucceed2>(Func<TSucceed, TSucceed2> projection)
=> _value;
public override string ToString() => $"Failure: {_value}";
}
private sealed class Succeed : Result<TFail, TSucceed>
{
private readonly TSucceed _value;
public Succeed(TSucceed value)
{
_value = value;
}
public override T Match<T>(Func<TFail, T> onFailure, Func<TSucceed, T> onSuccess)
=> onSuccess(_value);
public override void Match<T>(Action<TFail> onFailure, Action<TSucceed> onSuccess)
=> onSuccess(_value);
public override bool TryGetValue(out TSucceed value)
{
value = _value;
return true;
}
public override Result<TFail, TSucceed2> Map<TSucceed2>(Func<TSucceed, TSucceed2> projection)
=> projection(_value);
public override string ToString() => $"Success: {_value}";
}
/// <summary>
/// Implicitly convert success.
/// </summary>
public static implicit operator Result<TFail, TSucceed>(TSucceed success)
=> new Succeed(success);
/// <summary>
/// Implicitly convert failure.
/// </summary>
public static implicit operator Result<TFail, TSucceed>(TFail failure)
=> new Fail(failure);
}
public static class ResultExtensions
{
public static Result<TFail, TSuccess2> SelectMany<TFail, TSuccess, TSuccess2>(
this Result<TFail, TSuccess> me,
Func<TSuccess, Result<TFail, TSuccess2>> projection)
=> me.Match(f => f, projection);
public static Result<TFail, TResult> SelectMany<TFail, TSuccess, TSuccess2, TResult>(
this Result<TFail, TSuccess> me,
Func<TSuccess, Result<TFail, TSuccess2>> projection,
Func<TSuccess, TSuccess2, TResult> selector)
=> me.Match(f => f, s => projection(s).Match<Result<TFail, TResult>>(f2 => f2, s2 => selector(s, s2)));
public static Result<TFail, TSuccess2> Select<TFail, TSuccess, TSuccess2>(
this Result<TFail, TSuccess> me,
Func<TSuccess, TSuccess2> projection)
=> me.Map(projection);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment