Skip to content

Instantly share code, notes, and snippets.

@sander
Last active July 20, 2016 23:28
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 sander/8fc371deb26054a3e74b358e752ef510 to your computer and use it in GitHub Desktop.
Save sander/8fc371deb26054a3e74b358e752ef510 to your computer and use it in GitHub Desktop.
// Catch errors early
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace EithersAndOptionals
{
struct Either<T1, T2> where T1 : class where T2 : class
{
Either(T1 val1, T2 val2, Type type) { Value1 = val1; Value2 = val2; Which = type; }
public Either(T1 val) : this(val, null, typeof(T1)) { }
public Either(T2 val) : this(null, val, typeof(T2)) { }
T1 Value1 { get; }
T2 Value2 { get; }
Type Which { get; }
public TResult Map<TResult>(Func<T1, TResult> f1, Func<T2, TResult> f2) =>
Which == typeof(T1) ? f1(Value1) : f2(Value2);
public static implicit operator T1(Either<T1, T2> either) => either.Value1;
public static implicit operator T2(Either<T1, T2> either) => either.Value2;
public static implicit operator Type(Either<T1, T2> either) => either.Which;
public static implicit operator Either<T1, T2>(T1 val) => new Either<T1, T2>(val);
public static implicit operator Either<T1, T2>(T2 val) => new Either<T1, T2>(val);
public static implicit operator bool(Either<T1, T2> either) => either == typeof(T1);
}
struct NonNull<T> where T : class
{
NonNull(T val)
{
if (val == null)
throw new NullReferenceException();
else
Value = val;
}
public T Value { get; }
public static implicit operator T(NonNull<T> nn) => nn.Value;
public static implicit operator NonNull<T>(T v) => new NonNull<T>(v);
}
struct Optional<T> where T : class
{
Optional(T val) { Nullable = val; }
public T Nullable { get; }
public NonNull<T> Value => Nullable;
public static bool HasValue(Optional<T> opt) => opt.Nullable != null;
public static implicit operator bool(Optional<T> opt) => HasValue(opt);
public static implicit operator T(Optional<T> opt) => opt.Nullable;
public static implicit operator NonNull<T>(Optional<T> opt) => opt.Value;
public static implicit operator Optional<T>(T val) => new Optional<T>(val);
}
static class OptionalExtensions
{
public static IEnumerable<T> WithValues<T>(this IEnumerable<Optional<T>> opts) where T : class =>
opts.Where(Optional<T>.HasValue).Select(opt => (T)opt);
}
class A
{
public int Foo() => 42;
}
class Program
{
static void Main(string[] args)
{
TestEither();
TestOptional();
}
static void TestOptional()
{
var values = new[] { MakeA(), MakeNoA() };
foreach (var opt in values)
{
var result = ((A)opt)?.Foo();
if (opt)
{
Debug.Assert(result == 42);
Debug.Assert((A)opt == (A)opt.Value);
Debug.Assert(opt.Nullable?.Foo() == 42);
Debug.Assert(((A)opt.Value).Foo() == 42);
Debug.Assert(opt.Nullable.Foo() == 42);
}
else
{
Debug.Assert(result == null);
Debug.Assert(opt.Nullable == null);
}
}
Debug.Assert(values.WithValues().Select(a => a.Foo()).Count() == 1);
try
{
var x = MakeNoA().Value;
Debug.Assert(false);
}
catch (NullReferenceException)
{
}
}
static Optional<A> MakeA() => new A();
static Optional<A> MakeNoA() => null;
static void TestEither()
{
var values = new[] { MakeFirst(), MakeSecond() };
foreach (var x in values)
{
Debug.Assert(x.Map(a => a.Foo() == 42, s => s == "foo"));
if (x == typeof(string))
{
string s = x;
Debug.Assert(!x);
Debug.Assert(s == "foo");
Debug.Assert(x == "foo");
}
else
{
A a = x;
Debug.Assert(x);
Debug.Assert(a.Foo() == 42);
}
}
}
static Either<A, string> MakeFirst() => new A();
static Either<A, string> MakeSecond() => "foo";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment