Skip to content

Instantly share code, notes, and snippets.

@louthy
Last active September 1, 2016 02:06
Show Gist options
  • Save louthy/a774a3c061d70a1ee79d440684cb4504 to your computer and use it in GitHub Desktop.
Save louthy/a774a3c061d70a1ee79d440684cb4504 to your computer and use it in GitHub Desktop.
using System;
namespace TestTypeClasses
{
class Program
{
static void Main(string[] args)
{
var ma = Option.Some(2);
var mb = Option.Some(4);
var mc = Option.None<int>();
var md = from x in ma
from y in mb
select x + y;
Debug.Assert(md.Value is Some<int> && (md.Value as Some<int>).Value == 6);
var me = from x in ma
from y in mc
select x + y;
Debug.Assert(me.Value is None<int>);
}
}
public static class Option
{
public static M<OptionM<A>, Option<A>, A> Some<A>(A value) =>
default(OptionM<A>).Return<OptionM<A>>(value);
public static M<OptionM<A>, Option<A>, A> None<A>() =>
default(OptionM<A>).Fail<OptionM<A>>();
public static M<OptionM<B>, Option<B>, B> Select<A, B>(this M<OptionM<A>, Option<A>, A> self, Func<A, B> map) =>
self.Select<OptionM<A>, OptionM<B>, Option<A>, Option<B>, A, B>(map);
public static M<OptionM<C>, Option<C>, C> SelectMany<A, B, C>(
this M<OptionM<A>, Option<A>, A> self,
Func<A, M<OptionM<B>, Option<B>, B>> bind,
Func<A, B, C> project) =>
self.SelectMany<OptionM<A>, OptionM<B>, OptionM<C>, Option<A>, Option<B>, Option<C>, A, B, C>(bind, project);
}
/// <summary>
/// Monad type-class
/// </summary>
/// <typeparam name="VA">Higher order type for A</typeparam>
/// <typeparam name="A">Value type</typeparam>
public interface Monad<VA, A>
{
/// <summary>
/// Monad return
/// </summary>
M<MA, VA, A> Return<MA>(A value) where MA : struct, Monad<VA, A>;
/// <summary>
/// Monad fail
/// </summary>
M<MA, VA, A> Fail<MA>(string err = "") where MA : struct, Monad<VA, A>;
/// <summary>
/// Functor map
/// </summary>
M<MB, VB, B> Map<MA, MB, VB, B>(M<MA, VA, A> ma, Func<A, B> map)
where MA : struct, Monad<VA, A>
where MB : struct, Monad<VB, B>;
/// <summary>
/// Monadic bind
/// </summary>
M<MB, VB, B> Bind<MA, MB, VB, B>(M<MA, VA, A> ma, Func<A, M<MB, VB, B>> bind)
where MA : struct, Monad<VA, A>
where MB : struct, Monad<VB, B>;
}
public class M<MA, VA, A> where MA : Monad<VA, A>
{
public readonly VA Value;
public M(VA v)
{
Value = v;
}
}
/// <summary>
/// Option monad instance
/// </summary>
/// <typeparam name="A">Value type</typeparam>
public struct OptionM<A> : Monad<Option<A>, A>
{
public M<MB, VB, B> Bind<MA, MB, VB, B>(M<MA, Option<A>, A> ma, Func<A, M<MB, VB, B>> bind)
where MA : struct, Monad<Option<A>, A>
where MB : struct, Monad<VB, B> =>
ma.Value is Some<A>
? bind((ma.Value as Some<A>).Value)
: default(MB).Fail<MB>();
public M<MA, Option<A>, A> Fail<MA>(string err = "")
where MA : struct, Monad<Option<A>, A> =>
new M<MA, Option<A>, A>(Option<A>.None);
public M<MB, VB, B> Map<MA, MB, VB, B>(M<MA, Option<A>, A> ma, Func<A, B> map)
where MA : struct, Monad<Option<A>, A>
where MB : struct, Monad<VB, B> =>
ma.Value is Some<A>
? default(MB).Return<MB>(map((ma.Value as Some<A>).Value))
: default(MB).Fail<MB>();
public M<MA, Option<A>, A> Return<MA>(A value)
where MA : struct, Monad<Option<A>, A> =>
new M<MA, Option<A>, A>(Option<A>.Some(value));
}
/// <summary>
/// Option value (quick and dirty 'discriminated union')
/// </summary>
public abstract class Option<A>
{
public static Option<A> Some(A x) =>
new Some<A>(x);
public readonly static Option<A> None =
new None<A>();
}
/// <summary>
/// Some case
/// </summary>
public class Some<A> : Option<A>
{
public readonly A Value;
public Some(A value)
{
Value = value;
}
}
/// <summary>
/// None case
/// </summary>
public class None<A> : Option<A>
{
public None() { }
}
public static class Ext
{
/// <summary>
/// Generic Select (doesn't work with LINQ)
/// </summary>
public static M<MB, VB, B> Select<MA, MB, VA, VB, A, B>(
this M<MA, VA, A> self,
Func<A, B> map)
where MA : struct, Monad<VA, A>
where MB : struct, Monad<VB, B> =>
default(MA).Map<MA, MB, VB, B>(self, map);
/// <summary>
/// Generic SelectMany (doesn't work with LINQ)
/// </summary>
public static M<MC, VC, C> SelectMany<MA, MB, MC, VA, VB, VC, A, B, C>(
this M<MA, VA, A> self,
Func<A, M<MB, VB, B>> bind,
Func<A, B, C> project)
where MA : struct, Monad<VA, A>
where MB : struct, Monad<VB, B>
where MC : struct, Monad<VC, C> =>
default(MA).Bind(self, a =>
default(MB).Bind(bind(a), b =>
default(MC).Return<MC>(project(a, b))));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment