Maybe From Murray Monads
Off and on for the last six months, I have probably seen and read at least a half dozen introductions and tutorials on monads, a functional programming construct based on the mathematical monads from category theory. Monads are known for their headache-inducing explanations, and I’ve had my fair share. Every tutorial I’ve committed time to mentions the endless possibilities of using such constructs to improve your code, but I’ve had a hard time finding real-world examples of using this stuff out in the wild (much like this Stack Overflow poster, whose link I only found just now). It has recently become a quest of mine to find some C# code we all write that could be improved by a monad.
I am now going to attempt to introduce a Maybe monad that I cooked up, but I warn you that I have no idea what I’m doing or if I’m staying within the rules for what makes a monad by definition a monad. Don’t worry though, you have nothing to lose; I’m the only one taking a risk at looking foolish, and it’s all at the expense of my reputation and not yours. Should be fun!
“It is not the spoon that bends, it is only yourself”
From the Wikipedia article linked to above:
“In functional programming, a monad is a kind of abstract data type constructor used to represent computations (instead of data in the domain model). Monads allow the programmer to chain actions together to build a pipeline, in which each action is decorated with additional processing rules provided by the monad. Programs written in functional style can make use of monads to structure procedures that include sequenced operations, or to define arbitrary control flows (like handling concurrency, continuations, or exceptions).
…
“A programmer will compose monadic functions to define a data-processing pipeline. The monad acts as a framework, as it’s a reusable behavior that decides the order in which the specific monadic functions in the pipeline are called, and manages all the undercover work required by the computation.”
The promised tenets of monads are increased code readability and algorithm composability, an example of preferring declarative code over imperative, procedural code. In the case of the Maybe monad, it is a wrapper around types that might have an invalid state like null or nothing. The lure of the Maybe monad is the ability to deal with values that might be in this invalid state without having to explicitly check for null before continuing or exceptions being thrown in your algorithms.
I first found a post by Rinat Abdullin where he introduced his version of a C# Maybe monad. I downloaded the library and attempted to figure out how to use it in code I could possibly write any given day. I tried mimicking the HttpContext.Request.Params collection commonly used in ASP.NET pages and HTTP handlers to retrieve the passed in query string or post values on the Request object. If you ask that collection for a parameter that was not passed in on the request, it returns a null instead of the string value. Common practice is to then use a bunch of if statements and key off the fact that certain query string or post parameters may or may not exist.
The problem I ran into with Rinat’s implemenation is that I wrote the code using the monad how I thought it should help me, and then ran into compiler errors and runtime exceptions because of the expectation that Maybe objects couldn’t be constructed around null values. Here is the twitter conversation I had with Rinat in regard to this misconception that I had:
murrayondotnet says:
abdullin says:
murrayondotnet says:
abdullin says:
murrayondotnet says:
abdullin says:
murrayondotnet says:
And so I did.
Introducing MurrayMonads
I don’t even like the name. I’m also not sure this code will ever be used again…by anyone, let alone me (especially since I like some other guy’s implementation better; read on to watch me embarrass myself). But whatever, Visual Studio asks for a project/solution name and so there it is. I need some cool theme like Rhino, as used by Ayende.
Speaking of Oren (I will use his proper name instead so that he doesn’t get stuck at the airport unable to board his flight), he posted a link on Twitter to an article where Dmitri Nesteruk created his own version of the Maybe monad, just as I was starting my experiment. He used extension methods instead of an explicit monad object wrapper in order to enable the pipelining composability that you can also do with Rinat’s implementation. Really clever, but there was one small thing that bothered me a bit, and that is how the monad chaining begins with Dmitri’s API. (Actually, I’ll prove I’m wrong by the end of the post; this is the cause of upcoming embarrassment mentioned above, hooray!) Because of this, I decided to use his example problem as the basis for my sample that I will show, in order to increase comparability.
So I set off to try and merge the good points of both Rinat’s and Dmitri’s implementations. I have put my source code up for viewing at Bitbucket.org: http://bitbucket.org/murrayondotnet/murraymonads
Are We There Yet?!
Yes, we are. Let’s see some code finally.
Here is the code we don’t like writing all the time:
string postCode; if (person != null) { if (HasMedicalRecord(person) && person.Address != null) { CheckAddress(person.Address); if (person.Address.PostCode != null) postCode = person.Address.PostCode.ToString(); else postCode = "UNKNOWN"; } }
This is actually code that you wrote; I took it just yesterday from your current project at work. I know you’re feeling a bit defensive because of this, but I’m here to help so put down the attitude please. We’re gonna make your life easier…I hope…
All in all, it’s code you see every day and there’s not much really wrong with it per se. I just think it could be cleaner I suppose. So using Dmitri’s extension methods, you can get that logic down to this:
string postCode = this.With(x => person) .If(x => HasMedicalRecord(x)) .With(x => x.Address) .Do(x => CheckAddress(x)) .With(x => x.PostCode) .Return(x => x.ToString(), "UNKNOWN");
As long as that last ToString method call doesn’t return a null (which one of my use cases does in the TestConsoleApp included in my source code), you will either get the value or “UNKNOWN”. Looking good!
So my version of the Maybe monad comes out pretty close to this last one:
string postCode = person.If(p => HasMedicalRecord(p)) .Access(p => p.Address) .Apply(a => CheckAddress(a)) .Access(a => a.PostCode) .Convert(pc => pc.ToString(), "UNKNOWN"); // or even string postalCode = person.If(HasMedicalRecord) .Access(p => p.Address) .Apply(CheckAddress) .Access(a => a.PostCode) .Convert(pc => pc.ToString(), "UNKNOWN");
The difference between Dmitri’s and mine being how the chain is first started, on the Maybe person object instead of Dmitri’s use of the this keyword.
Here’s what you’ve been really waiting for where I eat my own words. Dmitri’s code can also be written like this:
string postCode = person.If(HasMedicalRecord) .With(x => x.Address) .Do(CheckAddress) .With(x => x.PostCode) .Return(x => x.ToString(), "UNKNOWN");
…which appears to take away any advantage of my library. I officially like his solution better, I just don’t like some of the method names. So perhaps I will create another blog post and Bitbucket repo with a new library of extension methods instead. I will leave up this mess-of-a-post public (as well as the source code) so that perhaps others can see the process I went through and learn from my mistakes.
Your Turn to Criticize Now
Let me have it. Let me know how crazy I am or if this is even useful. Let me know if I’m completely misusing the monad principles. Or, by some weird miracle, let me know if I’m on to something. Actually, if Dmitri is on to something. Either way, I want to hear from you. Thanks.