Skip to content

Instantly share code, notes, and snippets.

@dtchepak
Created April 23, 2013 22:29
Show Gist options
  • Save dtchepak/5447958 to your computer and use it in GitHub Desktop.
Save dtchepak/5447958 to your computer and use it in GitHub Desktop.
The @xerxesb enum catamorphism. (Ignore the equality members boilerplate)
using System;
using NUnit.Framework;
using Shouldly;
public class Mushroom { }
public sealed class Xenum : IEquatable<Xenum> {
private readonly Mushroom _mushroom;
private readonly string _s;
private readonly int _a;
private readonly int _b;
private readonly int _type;
private Xenum(int a, int b) { _a = a; _b = b; _type = 0; }
private Xenum(string s) { _s = s; _type = 1; }
private Xenum(Mushroom mushroom) { _mushroom = mushroom; _type = 2; }
public static Xenum SomeNumbers(int a, int b) { return new Xenum(a, b); }
public static Xenum AString(string s) { return new Xenum(s); }
public static Xenum BadgerBadgerBadger(Mushroom m) { return new Xenum(m); }
public static T Cata<T>(Xenum x, Func<int, int, T> someNumbers, Func<string, T> aString, Func<Mushroom, T> badgers) {
switch (x._type) {
case 0: return someNumbers(x._a, x._b);
case 1: return aString(x._s);
case 2: return badgers(x._mushroom);
default: throw new InvalidOperationException();
}
}
public bool Equals(Xenum other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(_mushroom, other._mushroom) && string.Equals(_s, other._s) && _a == other._a && _b == other._b && _type == other._type;
}
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return obj is Xenum && Equals((Xenum) obj);
}
public override int GetHashCode() {
unchecked {
var hashCode = (_mushroom != null ? _mushroom.GetHashCode() : 0);
hashCode = (hashCode*397) ^ (_s != null ? _s.GetHashCode() : 0);
hashCode = (hashCode*397) ^ _a;
hashCode = (hashCode*397) ^ _b;
hashCode = (hashCode*397) ^ _type;
return hashCode;
}
}
}
public class XenumTests {
[Test]
public void IdentityFunctionWhenGivenOriginalCtorFunctions() {
/* "A function that produces an identity, given constructors for a data type, is called its catamorphism."
* src: @dibblego, http://dl.dropboxusercontent.com/u/7810909/media/doc/list-folds.pdf */
var numbers = Xenum.SomeNumbers(6, 7);
var astring = Xenum.AString("hello world");
var badgers = Xenum.BadgerBadgerBadger(new Mushroom());
Func<Xenum, Xenum> idFunc = x => Xenum.Cata(x, Xenum.SomeNumbers, Xenum.AString, Xenum.BadgerBadgerBadger);
numbers.ShouldBe(idFunc(numbers));
astring.ShouldBe(idFunc(astring));
badgers.ShouldBe(idFunc(badgers));
}
[Test]
public void ConvertAnyXenumToSomeValue() {
var numbers = Xenum.SomeNumbers(6, 7);
var astring = Xenum.AString("hello world");
var badgers = Xenum.BadgerBadgerBadger(new Mushroom());
Func<Xenum, string> describe = x => Xenum.Cata(x, (a,b) => "numbers", s => "string", m => "mushroom");
describe(numbers).ShouldBe("numbers");
describe(astring).ShouldBe("string");
describe(badgers).ShouldBe("mushroom");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment