Skip to content

Instantly share code, notes, and snippets.

@dvdsgl
Last active January 8, 2024 06:11
Show Gist options
  • Star 25 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save dvdsgl/669d38dd067f799473fa to your computer and use it in GitHub Desktop.
Save dvdsgl/669d38dd067f799473fa to your computer and use it in GitHub Desktop.
Monads explained (sort of) to a C# developer

A monad is a fancy word for a generic type of the form MyMonad<T> (a generic type of arity 1).

A monad is special because it adds 'special powers' to the T that it wraps. These 'special powers' won't sound very special to an imperative programmer, so you have to squint to see them but bear with me.

  • IEnumerable<T> is a monad that gives values of type T the special power of nondeterminism, or the ability to 'be' multiple values at once.
  • Nullable<T> is a monad that gives values of type T the special power of nullability, or the ability to be absent.
  • Task<T> is a monad that gives values of type T the special power of asynchronicity, or the ability to be used before they are computed.

The trick with monads comes when you want to play with the T values, because they are inside another type. C# introduced language changes to make dealing with values inside these monads easier:

  • For IEnumerable<T>, we use LINQ to work with the T values inside.
  • For Nullable<T>, we don't really have good language support–you have to use ?? or .GetValue* to 'escape' from the Nullable<T>––it's awkward.
  • For Task<T>, we have await to let us play with the T values directly.

In languages like Haskell or F#, you have a general purpose way to define your own monads and define how to play with the values inside them. This is accomplished by defining two methods, usually called Return and Bind:

// Return lets you put a T inside the monad:
public static MyMonad<T> Return<T>(T value);

// Bind lets you get a take a MyMonad<T>, get at the T inside it,
// and turn it into a MyMonad<U>:
public static MyMonad<U> Bind<T, U>(MyMonad<T> value, Func<T, MyMonad<U>> operation);

In Haskell, these signatures look more or less like:

class Monad m where
  return :: a -> m a
  bind :: m a -> (a -> m b) -> m b

For example, Return lets you make a Task<string> from a string; Bind lets you take the string out of Task<string>, and turn it into a Task<UIImage>.

Monads are used extensively in functional programming to add 'special powers' like exceptions, async, state, logging, etc. to the exact regions of your code where you need them. Haskell, for example, is a much smaller language than C# because it doesn't allow values to be null, it doesn't allow mutation, it doesn't have exceptions, and it doesn't have LINQ; however, you can define these features (and more--e.g. massively parallel cloud computing) with monads and use them precisely.

@Mike-E-angelo
Copy link

Very cool. I am interested in F# and enjoy reading about these sorts of things. At some point I will make the leap. :) Thanks for posting this!

@louthy
Copy link

louthy commented Aug 19, 2015

Neither Task<T> nor Nullable<T> are monads, because they don't implement bind (or SelectMany as it's known in LINQ parlance), which is a pre-requisite. Seems a strange couple of examples to demonstrate monads, no?

For anyone reading this that would like to see full implementation of various monads in C#, I have a couple of libraries.

https://github.com/louthy/language-ext - this is more active, and has more 'complete' implementations
https://github.com/louthy/csharp-monad - this is older, still useful and includes the Parser Combinator monad, and linked to by wikipedia for examples of C# monads.

@Salgat
Copy link

Salgat commented Nov 26, 2017

@louthy Converting a task over to a "true" monad is pretty trivial so it's a fair comparison. https://ruudvanasseldonk.com/2013/05/01/the-task-monad-in-csharp

@desarrollo03TR
Copy link

Linq methods (selectmany, where, etc) can be implemented via extensions methods, then, Task and Nullable can be monads

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment