Skip to content

Instantly share code, notes, and snippets.

@tomlokhorst
Created February 23, 2010 21:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tomlokhorst/312713 to your computer and use it in GitHub Desktop.
Save tomlokhorst/312713 to your computer and use it in GitHub Desktop.
Monad type class ported to C#
// 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);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment