Last active
September 1, 2016 02:06
-
-
Save louthy/a774a3c061d70a1ee79d440684cb4504 to your computer and use it in GitHub Desktop.
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 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