public
Last active

Monad type class ported to C#

  • Download Gist
MonadTest.cs
C#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
// Monad type class ported to C#
// Only tested with: Mono C# compiler version 2.4.2.3
//
// The Monad type class has been split up into two .NET interface:
// - IMonad<A> has functions that can be applied _to_ monads (i.e. bind)
// - IMonadDictionary<A> has functions that _create_ monads (i.e. unit)
//
// Because .NET doesn't support higher kinded generics, we loose a bit of
// type safety. After calling `bind`, the return value has to be casted to
// the proper type.
//
// Also see:
// - Functor type class in C#: http://gist.github.com/311058
// (demonstrates dynamic casting after `fmap`)
// - Read type class in C#: http://gist.github.com/312509
// (demonstrates dictionaries for `read`)
 
using System;
 
// class Monad m where
// bind :: m a -> (a -> m b) -> m b -- (>>=)
// unit :: a -> m a -- return
 
interface IMonad<A>
{
IMonad<B> bind<B>(Func<A, IMonad<B>> f);
}
 
interface IMonadDictionary<A>
{
IMonad<A> unit(A x);
}
 
class MonadTest
{
public static void Main()
{
var maybe = new MaybeDictionary<int>();
 
var mx = new Maybe<int>(4);
var my = new Maybe<int>(3);
 
Func<int, int, int> add = (x, y) => x + y;
 
Maybe<int> mz = (Maybe<int>) liftM2(maybe, add, mx, my);
// The cast is necessary, to get back at Maybe<int> type.
// This cast is dynamically checked by the runtime.
 
if (mz.HasValue)
Console.WriteLine(mz);
// Multiplying 5 by Nothing
var ma = new Maybe<int>(5);
var mb = new Maybe<int>();
var mc = (Maybe<int>) liftM2(maybe, (x, y) => x * y, ma, mb);
 
Console.WriteLine(mc);
}
 
// liftM2, a common operation on monads
// Requires one additional dictionary object.
public static IMonad<C> liftM2<A, B, C>(IMonadDictionary<C> dict, Func<A, B, C> f, IMonad<A> mx, IMonad<B> my)
{
// Separately defined, due to bug in Mono
Func<A, Func<B, IMonad<C>>> g = x => y => dict.unit(f(x, y));
Func<A, IMonad<C>> h = x => my.bind(g(x));
return mx.bind(h);
}
}
 
class Maybe<A> : IMonad<A>
{
public IMonad<B> bind<B>(Func<A, IMonad<B>> f)
{
if (!HasValue)
return new Maybe<B>();
else
return f(value);
}
 
public bool HasValue { get; private set; }
 
public Maybe() {}
public Maybe(A x)
{
value = x;
HasValue = true;
}
 
public override string ToString()
{
return !HasValue ? "Nothing" : "Just " + value.ToString();
}
 
A value;
}
 
class MaybeDictionary<A> : IMonadDictionary<A>
{
public IMonad<A> unit(A x)
{
return new Maybe<A>(x);
}
}

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.