Monads are a hot topic in functional programming these days.
We’re all familiar with different types of operation chaining, which can look like:
Or another way, setting up function pipelines, based on one (or more) methods with the same signature.
But monads are even more powerful. They take a number of functions with different signatures, and map an input space onto an output space, using a core set of rules for governance.
You can think of a monad as: a problem space, functions that map from inputs into that space, and a “binding” rule that governs those functions.
If you have a big, complicated function that maps from a bunch of variables (the input space) to a bunch of variables (the output space) creating a monad is the same as breaking that big function into a bunch of independent, composable functions. These smaller functions are goverened by the monad so they play nice together.
Plus the monad can track information outside the values of the type it extends. For example, you can add a boolen “errror” field to String handling to handle errors without throwing exceptions.
The primary elements needed for a monad are:
- a group of target values
- functions that map inputs into those values
- a “bind” function that invokes those functions, and enforces global rules between them
- a “unit” or “return” function that kicks things off
Once you have these composable functions, and the bind to rule them (in the darkness, I suppose), then a complicated problem breaks down into a number of simple problems.
Better yet, the simple problems can be arranged, new ones added, and so forth, giving you a high degree of flexibility and extendability in your code.
In the programming realm, monads are a design pattern. They implement the pieces listed above.
My first cut wasn’t quite right, because I had the bind() function mapping back to the same class. So watch my blog for an upcoming example of a monad that does downcasts safely.