Maybe with Linq

Not long ago I wrote a post on using Maybe not null. In that post however I did not provide an implementation of the Maybe monad. There are a few floating around the internet (including the Maybe package on nuget) but it’s fun and I think it’s worth showing my own just for the sake of it. Here is my simplified version of the Maybe monad in C#:

Download on skydrive

using System;

namespace System.Monads
{
public interface IMaybe<T>
{
bool HasValue { get; }
T Value { get; }
}

public class Maybe<T> : IMaybe<T>
{
public static readonly Maybe<T> Nothing = new Maybe<T>();

public bool HasValue { get; private set; }
public T Value { get; private set; }

private Maybe()
{
HasValue = false;
Value = default(T);
}

internal Maybe(T value)
{
HasValue = !object.Equals(value, null);
Value = value;
}

public static implicit operator Maybe<T>(T value)
{
return new Maybe<T>(value);
}
}

public static class Maybe
{
public static IMaybe<T> ToMaybe<T>(this T value)
{
IMaybe<T> maybe = value as IMaybe<T>;
if (maybe != null)
return maybe;

return new Maybe<T>(value);
}

// Monadic Bind operator
public static IMaybe<TResult> Bind<TSource, TResult>(this IMaybe<TSource> m, Func<TSource, IMaybe<TResult>> f)
{
if (!m.HasValue)
return Maybe<TResult>.Nothing;

return f(m.Value);
}

public static IMaybe<TResult> Select<TSource, TResult>(this IMaybe<TSource> m, Func<TSource, TResult> f)
{
return m.Bind(x => f(x).ToMaybe());
}

public static IMaybe<TResult> SelectMany<TSource, TResult>(this IMaybe<TSource> m, Func<TSource, TResult> f)
{
return m.Bind(x => f(x).ToMaybe());
}

public static IMaybe<TResult> SelectMany<TSource, TMaybe, TResult>(this IMaybe<TSource> m, Func<TSource, IMaybe<TMaybe>> f, Func<TSource, TMaybe, TResult> g)
{
return m.Bind(x => f(x).Bind(y => g(x, y).ToMaybe()));
}

// Simplified form, check for null or use Nullable<T>.HasValue instead of Maybe.
// This form is not as strictly correct but it results in a more useable syntax in C#.
public static TResult Select<TSource, TResult>(this TSource m, Func<TSource, TResult> f)
{
return m.ToMaybe().Bind(x => f(x).ToMaybe()).Value;
}

public static TResult SelectMany<TSource, TResult>(this TSource m, Func<TSource, TResult> f)
{
return m.ToMaybe().Bind(x => f(x).ToMaybe()).Value;
}

public static TResult SelectMany<TSource, TMaybe, TResult>(this TSource m, Func<TSource, TMaybe> f, Func<TSource, TMaybe, TResult> g)
{
return m.ToMaybe().Bind(x => f(x).ToMaybe().Bind(y => g(x, y).ToMaybe())).Value;
}
}
}

It’s a pretty simple implementation, I tried to stay true to it’s monadic nature by creating the Bind method with the correct function signature but I also created a number of Select and SelectMany functions which allow you to use Linq syntax to bind to your Maybe monad.
 
I also created a few non-maybe functions which allow you to bind to regular objects without needing to wrap it in a maybe object. If your object is a value type then it will never be null anyway and if you use a reference type (including Nullable<T>) then you do a null check on the result instead of HasValue check. Strictly speaking that isn’t the purest version of a Monad but with the syntax limitations of C# I think it results in the cleanest usage possible. It’s a reasonable compromise.
 
Here are some examples of how you can use it:
// Most verbose form, most strictly correct
var m1 = 5
.ToMaybe()
.Select(x => x + 10);

// prettier but result will be of type int
var m2 = from x in 5
select x + 10;

// if you use nullable structs, it's pretty and result will be HasValue=false if anything is null
var m3 = from x in (int?)5
from y in (int?)10
select x + y;

// identical to above except the long form, with a null
int? five = 5;
int? ten = null;
var m4 = five.Select(x => ten.SelectMany(y => x + y));

// Pretty form for reference types, check for null on result.
var m5 = from x in "testing"
from y in (string)null
select x + y;

// Maybe form of above, check for HasValue
var m6 = from x in "testing".ToMaybe()
from y in ((string)null).ToMaybe()
select x + y;

// Longer select chains
var m7 = from x in new Example()
from y in x.Inner
from z in y.Inner
select z.Value;

var m8 = from x in new Example { Inner = new Example { Inner = new Example { Value = "success!" } } }
from y in x.Inner
from z in y.Inner
select z.Value;

Console.WriteLine("m1: {0}->{1}", m1.HasValue, m1.Value);
Console.WriteLine("m2: True->{0} (non-nullable)", m2);
Console.WriteLine("m3: {0}->{1}", m3.HasValue, m3.Value);
Console.WriteLine("m4: {0}", m4.HasValue);
Console.WriteLine("m5: {0}", m5 != null);
Console.WriteLine("m6: {0}", m6.HasValue);
Console.WriteLine("m7: {0}", m7 != null);
Console.WriteLine("m8: {0}->{1}", m8 != null, m8);
Console.ReadKey(true);

Advertisements

3 thoughts on “Maybe with Linq

  1. Pingback: Maybe with Linq | Software Development Hub | Scoop.it

  2. I liked your implementation.
    I have one suggestion: you could make the IMaybe interface covariant (out T). That way you’ll be able to add an IMaybe to a list of List<IMaybe> .

Drop a brain bomb

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s