Maybe Monad… my C# version
Functional programming isn’t a new concept. There is Scheme, Haskell and a bunch of other really cool languages. But new on the block is F#. F# is Microsoft’s stab at creating a functional programming in the .NET world as a first-class language.
Functional programming paradigms are increasingly important to all .NET developers. This post will detail the functional elements (in my mind) of the Maybe Monad, something I’m sure everyone has written in some way or another (ok maybe not everyone, but there are certainly a lot of implementations out there in C#). But let’s discuss the application, functionally, of the maybe monad.
The Basics
There are three areas of the maybe monad that are really important. The first is the concept of “Nothing” and “Just”. Nothing, in this context, would be either a failed operation (say division by 0, for example) or something that just returns null. The “Just” is the opposite of “Nothing”, it’s something. It’s a successful operation that returns a value. Although this concept seems simple, it’s actually quite powerful. We’ll return to this in a bit.
The second of the important concepts with the maybe monad is the “bind” and “return” verbs. “Bind” is the assigning of a value to a Maybe. “Return” the extraction of the value from the maybe. See the Maybe Monad is a container of a value (or lack thereof). The verbs attached to the container relay a façade of the actual value. While my implementation doesn’t deal with bind/return verbs directly, it is used indirectly with the composition and decomposition of the value container.
The third is something we’re ALL familiar with, a type constraint. The formal declaration of the maybe monad declares that there must be a type parameter of type M. This letter is arbitrary, of course, but the concept is important. Without the constraint, the possible values of the true value bound to the maybe container would be limitless. One more reason where generics in C# are invaluable!
Let’s get coding!
Start with Nothing
Let’s start out with some initial xUnit.net tests.
[Fact]
public void Maybe_NothingContructor_SetsNothingFlag()
{
var maybe = new Maybe<int>();
bool isNothing = maybe.Nothing;
Assert.True(isNothing);
}
[Fact]
void Maybe_JustContructor_DoesntSetNothingFlag()
{
var maybe = new Maybe<int>(5);
bool isNothing = maybe.Nothing;
Assert.False(isNothing);
}
[Fact]
public void Maybe_JustContructor_SetsNothingFlag_WhenTheValuePassedIsNull()
{
var maybe = new Maybe<int>();
bool isNothing = maybe.Nothing;
Assert.True(isNothing);
}
As you can see there are 2 constructors. The default is the “nothing” constructor. The constructor with a value is the “nothing” if the value is null and the “just” if there is an actual value. This is an immutable container, so I don’t feel so bad setting these values only in the constructor. Here’s what my “Maybe” looks like now.
/// <summary>
/// Value container for storing
/// a) a value ("Just")
/// b) lack of a value ("Nothing")
/// </summary>
/// <typeparam name="M">Type M of the Maybe Monad</typeparam>
public sealed class Maybe<M>
{
/// <summary>
/// Non-value maybe ("Nothing")
/// </summary>
public Maybe()
{
// assign the nothing flag
Nothing = true;
}
/// <summary>
/// Maybe with a value ("Just")
/// </summary>
/// <param name="value"></param>
public Maybe(M value)
{
// assign the nothing flag
// when not null
Nothing = value == null;
}
/// <summary>
/// Determines the state of the
/// value in the container
/// </summary>
public bool Nothing { get; private set; }
}
You might be wondering why I made this class sealed. My rationale for this is that, since Maybe is immutable, we don’t want to get rid of some of the guarantees we have with the default implementation. Having a central “maybe” is a nice, simple way to define the maybe behavior.
Now for the return
The notion of a return for the maybe monad essentially means extracting the value from the container. I could define a “Return” function or even a “Value” property. But this is an unnecessary step. We don’t really need added complexity. My theory is that casting would be the best option. It is, after all, just a façade. Plus casting is a bit more expressive anyways. In your implementation, you can do this if you wish :)
[Fact]
public void Maybe_MCast_ReturnsAValue_WhenThereIsAValue()
{
var maybe = new Maybe<string>("");
string value = maybe;
Assert.NotNull(value);
}
[Fact]
public void Maybe_MCast_ReturnsSameValue_WhenThereIsAValue()
{
var initialValue = "";
var maybe = new Maybe<string>(initialValue);
string value = maybe;
Assert.Same(initialValue, value);
}
[Fact]
public void Maybe_MCast_ReturnsNull_WhenThereIsNotAValue()
{
var maybe = new Maybe<string>();
string value = maybe;
Assert.Null(value);
}
And here is the code that makes the tests pass.
public sealed class Maybe<M>
{
private M _value;
...
// previous code hidden for brevity
public static implicit operator M(Maybe<M> maybe)
{
if (maybe == null)
throw new ArgumentNullException("maybe");
return maybe._value;
}
}
I added the value field and the implicit cast operator.
Another way to bind
The constructor to define the “Just” and “Nothing” is useful, but what if we wanted save a little code when we are returning a maybe from a method? To do that, we’ll define another implicit operator. This time the operator will take an M and return a Maybe<M>. Here’s our test:
[Fact]
public void Maybe_MaybeCast_ReturnsNothingMaybe_WhenTheValueIsNull()
{
string val = null;
Maybe<string> maybe = val;
Assert.True(maybe.Nothing);
}
[Fact]
public void Maybe_MaybeCast_ReturnsJustMaybe_WhenTheValueIsNotNull()
{
string val = "";
Maybe<string> maybe = val;
Assert.False(maybe.Nothing);
}
And here’s the code.
public sealed class Maybe<M>
{
...
// previous code hidden for brevity
public static implicit operator Maybe<M>(M value)
{
return new Maybe<M>(value);
}
}
Apply for Chainability
Being able to chain different functions is really a key part of the maybe monad. The verb I will use is “Apply”. In functional languages, this action just happens due to function composition. We have two options here. We can return the Maybe monad as a Func<Maybe<M>, Maybe<M>> or we can define an Apply method to take a Func<Maybe<M>, Maybe<M2>> where M2 is essentially a new value. The Apply method could serve as a converter. If the initial value of M is Nothing, then the converted value should return a Nothing maybe monad. The great thing about the maybe monad is that there is no need to throw exceptions.Although the throwing of exceptions can provide useful information, most simple operations don’t require such complexity. Just test for Nothing on the maybe. Now you don't have to add a try/catch throughout your code.
I am going to use the Apply method. Although returning a Func<Maybe<M>, Maybe<M>> might add expressive syntax, the Apply method will be a little more flexible for most scenarios. Here’s some tests.
[Fact]
public void Maybe_Apply_ReturnsNothingMonad_WhenTheInitialNothingMaybe()
{
Func<Maybe<string>, Maybe<string>> fun= (m) => new Maybe<string>("");
Maybe<string> initialMaybe = new Maybe<string>();
Maybe<string> appliedMaybe = initialMaybe.Apply(fun);
Assert.True(appliedMaybe.Nothing);
}
[Fact]
public void Maybe_Apply_ReturnsNewMaybeWithNewValue()
{
Func<Maybe<string>, Maybe<string>> addBchar = (maybe) =>
new Maybe<string>(((string)maybe) + "b");
string initial = "", final = "b";
Maybe<string> initialMaybe = new Maybe<string>(initial);
string afterApply = initialMaybe.Apply(addBchar);
Assert.Equal(final, afterApply);
}
As you can see, my implementation will automatically return a nothing maybe monad when a nothing maybe monad is the initial value. OK here’s the code.
/// <summary>
/// Applies the specified function to this object
/// if this maybe is not nothing
/// </summary>
/// <typeparam name="M2">Return Type</typeparam>
/// <param name="applyFunction">Conversion function</param>
/// <returns>A Nothing maybe if the initial maybe is nothing
/// or the value returned by <see cref="applyFunction"/> if
/// this maybe has a value.
/// </returns>
public Maybe<M2> Apply<M2>(Func<Maybe<M>, Maybe<M2>> applyFunction)
{
if (Nothing)
return new Maybe<M2>();
return applyFunction(this);
}
Conclusion
The maybe monad is pretty cool for writing structure C# code. Your methods can now return a Maybe<M> to be perfectly clear if your methods returned actual values or null. This also gives you another definition of null. Perhaps your idea of “null” could be returning a 0. We have added flexibility with this new construct.
I’ve just hit the high marks in this post of my implementation of the maybe monad. Here’s an extended version with tests. Enjoy :)