Last time on FAIC I set out to explore monads from an object-oriented programmer’s perspective, rather than delving into the functional programmer’s perspective immediately. The “monad pattern” is a design pattern for types, and a “monad” is a type that uses that pattern. Rather than describing the pattern itself, let’s start by listing some monad-ish types that you are almost certainly very familiar with, and see what they have in common.

These five types are the ones that immediately come to my mind; I am probably missing some. If you have an example of a commonly-used C# type that is monadic in nature, please leave a comment.

Nullable<T> — represents a T that could be null

— represents a T that could be null Func<T> — represents a T that can be computed on demand

— represents a T that can be computed on demand Lazy<T> — represents a T that can be computed on demand once, then cached

— represents a T that can be computed on demand once, then cached Task<T> — represents a T that is being computed asynchronously and will be available in the future, if it isn’t already

— represents a T that is being computed asynchronously and will be available in the future, if it isn’t already IEnumerable<T> — represents an ordered, read-only sequence of zero or more Ts

So, what do these types have in common? The most obvious thing is that they are generic types with exactly one type parameter. Moreover, these types are embarrassingly generic. With the exception of Nullable<T> , all of these type work equally well with any T whatsoever; they are totally “agnostic” as to the semantics of their underlying type. And even Nullable<T> is only restricted to non-nullable value types.

Aside: This is essentially an accident of history. In a counterfactual world where the CLR had generic types from the get-go, it seems plausible that Nullable<T> could have been implemented to work on any type. We could have a type system where Nullable<string> was the only legal way to represent “a string that can be null”. Keep this in mind the next time you design a new type system!

Another way to look at these generic types is that they are “amplifiers”. (I am indebted to my erstwhile colleague Wes Dyer for this interpretation of monads; his article on monads was a crucial step in my understanding of the concept.) An “amplifier” is something that increases the representational power of their “underlying” type.

A byte can be one of 256 values; that’s very useful but also very simple. By using generic types we can represent “an asynchronously-computed sequence of nullable bytes” very easily: Task<IEnumerable<Nullable<byte>>> . That adds a huge amount of power to the “byte” type without changing its fundamental “byte-ish” nature.

So is a monad simply an embarrassingly generic type of one parameter that conceptually “adds power” to its underlying type? Not quite; there are a couple more things we need in order to have an implementation of the “monad pattern”. Next time on FAIC we’ll try to perform some operations on these five types and see if we can suss out any other commonality.