You probably wrote half a monad by accident

7 min read Original article ↗
Photo by Krzysztof Hepner on Unsplash

It’s the classic love story. Girl meets boy. Boy isn’t sure if his function will succeed, so he returns a metatype for his actual type indicating whether the operation succeeded or failed. Boy loses girl. We’ve all been there.

Here’s how it happened. Err, well, at least that second part.

You had a function for fetching Doohickeys. It looked like this:

public Doohickey GetDoohickey(string Id){/*...*/}

But pretty soon you realized that no Doohickey might exist, so you quickly revised your API to allow for a null return:

public Doohickey? GetDoohickey(string Id){/*...*/}

All was well. Fetching a Doohickey results in one of two things: null, or not-null. It’s great that C# has facilities for representing this! At least, that’s what you thought before an ominous cloud appeared overhead and a security review reminded you that not everyone has access to all Doohickeys. You recalled that the security team doesn’t reach out to say hello, they reach out to tell you to pack your shit, so you quickly add a permissions check to avoid the conversation:

public Doohickey? GetDoohickey(string Id)
{
  if (!PermissionCheck())
    // wtf do I return here?

  // ...
}

You were stuck then, because the type returned by your function only has two states: null or not null. If you return null, a caller might interpret that the Doohickey might simply not exist and do something unmentionable. You need a third state, but C# doesn’t support a Nullable<Nullable<Doohickey>> type because it doesn’t make sense and you felt a little guilty for even considering it.

“And is it really an exceptional scenario where a user doesn’t have permission to access a Doohickey?” You asked yourself. “Do I want to go down the rabbit hole of adding try-catch handlers everywhere in the code?”

So you had a brilliant idea:

“I’ll compose a new type that holds a Doohickey and also indicates whether the fetch operation succeeded!”

With one hand giddily patting yourself on the back, you used the other to write the following (on your one-handed Dvorak):

public class GetDoohickeyResult
{
    public Doohickey? TheDoohickey {get; set;}
    public bool Succeeded {get; set;}
}

public GetDooHickeyResult GetDoohickey(string Id)
{
  if (!PermissionCheck())
    return new GetDoohickeyResult{TheDoohickey = null, Succeeded = false};
  // ...
}

It wasn’t long before you realized this pattern could be applied to other things, so you created a generic type that could hold a Doohickey, a Thingamajig, a Whatchamacallit, or anything else. And you probably called that class Result<T>

public class Result<T>
{
    // Note: NOT the 1982 John Carpenter horror film... but actually kind of similar
    public T? TheThing {get; set;}
    public bool Succeeded {get; set;}
}

public Result<DooHickey> GetDoohickey(string Id){/*...*/}

Well, congratulations for getting that far. You made half a Monad. Or as functional programmers would call it, “half a monad” (but with a snooty, holier-than-thou tone).

A regular Doohickey can be shoveled into Result<Doohickey> just fine:

Doohickey myPrecious = FetchDoohickeyFromLake();
var successfulDoohickeyResult = new Result<DooHickey>
{
    TheThing = myPrecious,
    Succeeded = true;
}

Most of the programming world sees this as run-of-the-mill construction, but functional programmers fall to their knees and reverently proclaim “It is return! The first law of Monads!”. Some less-religious functional programmers might shrug and call it “lifting”. (Meme-loving functional programmers say you’ve rolled a burrito).

You probably stopped your journey here. The code was good enough, tests passed, and product was already cracking the whip for you to move on because the feature-factory culture of your organization was insatiable.

But maybe you didn’t move on. Maybe you thought, “Hey, I can take this farther.” Maybe you led the hoi polloi in an anti-capitalist revolution that overthrew the fat pigs on top of the social hierarchy, ushering in a new era of peace and prosperity!

But probably you only refactored a little bit more, which is enough rebellion for one day.

That refactoring was fueled by your dislike of a bunch of if statements cluttering up your business logic:

var maybeDoohickey = GetDoohickey(id);
if (maybeDoohickey.Success)
{
    // TODO: Hide in a cave
}

If you couldn’t have a utopia in the real world, then dammit you’d have one in your obscure codebase! It shall have clean logic untarnished by dirty, filthy hobbitses error-checking.

Something like this:

GetDoohickey(id)
.Then(HideInCave)
.Then(EatAFish);

Sure, sure, error-checking could happen at the end, after you’d already expressed your happy path:

GetDoohickey(id)
.Then(HideInCave)
.Then(EatAFish)
.OnError(StealFromHobbits);

So you wrote the following implementation of this magical .Then function. It accepts a function that will be called only if the provided Result<Doohickey> is successful. It might return a type other than Doohickey (represented by U), but it’s always possible that both the source and destination types (T and U respectively) could be Doohickey.

public static Result<U> Then<T, U>(this Result<T> result, Func<T, U> onSuccess)
{
  if (result.Success)
    return new Result<U>{TheThing = onSuccess(result.TheThing), Success = true};
  else
    return new Result<U>{TheThing = null, Success = false};
}

(If you knew too much theory you might have chosen a silly, confusing name like Map or Select)

Shortly after that, you realized that HideInCave should return a Result<Doohickey> too:

public Result<Doohickey> HideInCave(Doohickey thePrecious){/*...*/}

Putting it gently, this really fucked your code up. Now your .Then wasn’t returning a Result<Doohickey>, it was returning a Result<Result<Doohickey>> and the compiler didn’t like it:

GetDoohickey(id) // Result<Doohickey>
.Then(HideInCave) // Result<Result<Doohickey>>
.Then(EatAFish) // compiler error, `EatAFish` does not accept a parameter of type `Result<Doohickey>`
.OnError(StealFromHobbits)

Fortunately you had a simple enough fix for it. You’d write an overload for .Then where you didn’t need to construct your own Result<U> in the success case, you’d let the Func<T, Result<U>> parameter do it for you:

public static Result<U> Then<T, U>(this Result<T> result, Func<T, Result<U>> onSuccess)
{
  if (result.Success)
    // this next line just returns the Result<U> from onSuccess unlike the other .Then overload
    return onSuccess(result.TheThing);
  else
    return new Result<U>{TheThing = null, Success = false};
}

(If you knew too much theory again, you might have chosen a completely different nonsensical name for this function like Bind or SelectMany)

You code now automatically flattened Result<Result<T>> into Result<T>. Satisfied, you grabbed a nearby shop towel to wipe the grease off your elbows, chuckled at your silly hack, and moved on with your life.

But the devout functional programmer watching you (they see all) now prostrated themselves to the floor chanting in holy ecstasy “The prophecy has been fulfilled. The second law of Monads – Bind – has arrived! The reckoning is nigh!” (The meme-loving functional programmers would giggle that you unwrapped your burrito.)

If you really made it that far, congratulations, you accidentally created a real Monad. Or at least the closest approximation to one that your programming language supported.

Oh yeah, and while you were doing all that your girlfriend left you.

The End

This story is based on the reality of codebases I’ve seen in my career. Half-baked implementations of Monads that do just enough to be useful for one or two use cases, but not enough to really make the overall codebase easier to use. It is my great hope that this article de-mystifies the scary Monad word and other category theory terminology that gets thrown around with it.

The Result<T> example here is most closely associated with what might be called an Either Monad. In a “real” implementation of it, there would be a litany of other helper functions to account for all kinds of different cases, and I say that from experience writing a simple functional library to support my own work. I want to strongly dissuade you from attempting to write your own. Instead, reach for one of the established functional programming libraries out there for your programming language (For example, language-ext in C#).

Libraries can only do so much though, and when you go down this path you’ll quickly find their limitations. Before you know it, you’ll be filing issues on your language’s GitHub page, complaining about lack of functional support on StackOverflow, booting Arch Linux, sporting a top-hat and bowtie, and unironically saying “indubitably” in casual conversation. It’s a dangerous path to walk.

Until next time.