Skip to content

Instantly share code, notes, and snippets.

@manio143
Created May 5, 2020 20:22
Show Gist options
  • Save manio143/25987a179f72a1baf83f158d3814852d to your computer and use it in GitHub Desktop.
Save manio143/25987a179f72a1baf83f158d3814852d to your computer and use it in GitHub Desktop.
C# Sum algebraic types
using System;
namespace SumTypes
{
public abstract class Option<T> : SumType<Option<T>.Enum>
{
public enum Enum { None = 0, Some = 1 }
private Option(Enum tag) : base(tag) { }
public sealed class None : Option<T>
{
public None() : base(Enum.None) { }
public override string ToString() => "None";
}
public sealed class Some : Option<T>
{
public readonly T Value;
public Some(T value) : base(Enum.Some)
=> this.Value = value;
public override string ToString() => $"Some({Value})";
}
}
public static class OptionExt
{
public static Option<U> Map<T,U>(this Option<T> @this, Func<T, U> mapper)
{
switch (@this.Tag)
{
case Option<T>.Enum.None:
return new Option<U>.None();
case Option<T>.Enum.Some:
var x = @this.As<Option<T>.Some>().Value;
var nx = mapper(x);
return new Option<U>.Some(nx);
default: throw new PatternMatchException<Enum>(@this.Tag);
}
}
public static Option<U> Bind<T,U>(this Option<T> @this, Func<T, Option<U>> binder)
{
switch (@this.Tag)
{
case Option<T>.Enum.None:
return new Option<U>.None();
case Option<T>.Enum.Some:
var x = @this.As<Option<T>.Some>().Value;
return binder(x);
default: throw new PatternMatchException<Enum>(@this.Tag);
}
}
}
}
using System;
namespace SumTypes
{
class Program
{
static void Main(string[] args)
{
var zero = new Option<int>.Some(0);
var one = new Option<int>.Some(1);
var five = one.Map(x => x * 5);
Option<int> divideOver(int x)
{
if (x == 0)
return new Option<int>.None();
else
return new Option<int>.Some(10 / x);
}
Console.WriteLine("({0} >>= divideOver) = {1}", five, five.Bind<int,int>(divideOver));
Console.WriteLine("({0} >>= divideOver) = {1}", zero, zero.Bind<int,int>(divideOver));
}
}
}
using System.Runtime.CompilerServices;
namespace SumTypes
{
public abstract class SumType<TEnum> where TEnum : System.Enum
{
public readonly TEnum Tag;
protected SumType(TEnum tag) => this.Tag = tag;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public S As<S>() where S : SumType<TEnum>
{
var p = this;
return Unsafe.As<SumType<TEnum>, S>(ref p);
}
}
[System.Serializable]
public class PatternMatchException<TEnum> : System.Exception where TEnum : System.Enum
{
public PatternMatchException(TEnum e)
: base($"Couldn't match pattern with tag '{e}'")
{ }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment