Created
May 5, 2020 20:22
-
-
Save manio143/25987a179f72a1baf83f158d3814852d to your computer and use it in GitHub Desktop.
C# Sum algebraic types
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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