Lably
Skip to content

Monad (functional programming)

Design pattern in functional programming to build generic types

  • 50 min read
  • 6 chapters

Related on Lably

Through Interface (computing), Library (computing) and Input/output

Both the concept of a monad and the term originally come from category theory, where a monad is defined as an endofunctor with additional structure. Research beginning in the late 1980s and early 1990s established that monads could bring seemingly disparate computer-science problems under a unified, functional model. Category theory also provides a few formal requirements, known as the monad laws, which should be satisfied by any monad and can be used to verify monadic code.

Since monads make semantics explicit for a kind of computation, they can also be used to implement convenient language features. Some languages, such as Haskell, even offer pre-built definitions in their core libraries for the general monad structure and common instances.

Overview

"For a monad m, a value of type m a represents having access to a value of type a within the context of the monad." —C. A. McCann

More exactly, a monad can be used where unrestricted access to a value is inappropriate for reasons specific to the scenario. In the case of the Maybe monad, it is because the value may not exist. In the case of the input/output (IO) monad, it is because the value may not be known yet, such as when the monad represents user input that will only be provided after a prompt is displayed. In all cases the scenarios in which access makes sense are captured by the bind operation defined for the monad; for the Maybe monad a value is bound only if it exists, and for the IO monad a value is bound only after the prior operations in the sequence are performed.

A monad can be created by defining a type constructor M and two operations:

  • return :: a -> M a (often also called unit), which receives a value of type a and wraps it into a monadic value of type M a, and
  • bind :: (M a) -> (a -> M b) -> (M b) (typically represented as >>=), which receives a monadic value of type M a and a function f that accepts values of the base type a. Bind unwraps M a, applies f to it, and can process the result of f as a monadic value M b.

(An alternative but equivalent construct using the join function instead of the bind operator can be found in the later section § Derivation from functors.)

With these elements, the programmer composes a sequence of function calls (a "pipeline") with several bind operators chained together in an expression. Each function call transforms its input plain-type value, and the bind operator handles the returned monadic value, which is fed into the next step in the sequence.

Typically, the bind operator >>= may contain code unique to the monad that performs additional computation steps not available in the function received as a parameter. Between each pair of composed function calls, the bind operator can inject into the monadic value m a some additional information that is not accessible within the function f, and pass it along down the pipeline. It can also exert finer control of the flow of execution, for example by calling the function only under some conditions, or executing the function calls in a particular order.

An example: Maybe

One example of a monad is the Maybe type. Undefined null results are one particular pain point that many procedural languages don't provide specific tools for dealing with, requiring use of the null object pattern or checks to test for invalid values at each operation to handle undefined values. This causes bugs and makes it harder to build robust software that gracefully handles errors. The Maybe type forces the programmer to deal with these potentially undefined results by explicitly defining the two states of a result: Just ⌑result⌑, or Nothing. For example, the programmer might be constructing a parser, which is to return an intermediate result, or else signal a condition which the parser has detected, and which the programmer must also handle. With just a little extra functional spice on top, this Maybe type transforms into a fully-featured monad.

In most languages, the Maybe monad is also known as an option type, which is just a type that marks whether or not it contains a value. Typically they are expressed as some kind of enumerated type. In the Rust programming language it is called Option<T> and variants of this type can either be a value of generic type T, or the empty variant: None.

<span id="mwvA"></span><span class="c1" id="mwvQ">// The <T> represents a generic type "T"</span> <span class="k" id="mwvg">enum</span><span class="w" id="mwvw"> </span><span class="nb" id="mwwA">Option</span><span class="o" id="mwwQ"><</span><span class="n" id="mwwg">T</span><span class="o" id="mwww">></span><span class="w" id="mwxA"> </span><span class="p" id="mwxQ">{</span> <span class="w" id="mwxg"> </span><span class="nb" id="mwxw">Some</span><span class="p" id="mwyA">(</span><span class="n" id="mwyQ">T</span><span class="p" id="mwyg">),</span> <span class="w" id="mwyw"> </span><span class="nb" id="mwzA">None</span><span class="p" id="mwzQ">,</span> <span class="p" id="mwzg">}</span>

Option<T> can also be understood as a "wrapping" type, and this is where its connection to monads comes in. In languages with some form of the Maybe type, there are functions that aid in their use such as composing monadic functions with each other and testing if a Maybe contains a value.

In the following hard-coded example, a Maybe type is used as a result of functions that may fail, in this case the type returns nothing if there is a divide-by-zero.

<span id="mw1g"></span><span class="k" id="mw1w">fn</span><span class="w" id="mw2A"> </span><span class="nf" id="mw2Q">divide</span><span class="p" id="mw2g">(</span><span class="n" id="mw2w">x</span><span class="p" id="mw3A">:</span><span class="w" id="mw3Q"> </span><span class="nc" id="mw3g">Decimal</span><span class="p" id="mw3w">,</span><span class="w" id="mw4A"> </span><span class="n" id="mw4Q">y</span><span class="p" id="mw4g">:</span><span class="w" id="mw4w"> </span><span class="nc" id="mw5A">Decimal</span><span class="p" id="mw5Q">)</span><span class="w" id="mw5g"> </span><span class="p" id="mw5w">-></span><span class="w" id="mw6A"> </span><span class="nb" id="mw6Q">Option</span><span class="o" id="mw6g"><</span><span class="n" id="mw6w">Decimal</span><span class="o" id="mw7A">></span><span class="w" id="mw7Q"> </span><span class="p" id="mw7g">{</span> <span class="w" id="mw7w"> </span><span class="k" id="mw8A">if</span><span class="w" id="mw8Q"> </span><span class="n" id="mw8g">y</span><span class="w" id="mw8w"> </span><span class="o" id="mw9A">==</span><span class="w" id="mw9Q"> </span><span class="mi" id="mw9g">0</span><span class="w" id="mw9w"> </span><span class="p" id="mw-A">{</span> <span class="w" id="mw-Q"> </span><span class="nb" id="mw-g">None</span> <span class="w" id="mw-w"> </span><span class="p" id="mw_A">}</span><span class="w" id="mw_Q"> </span><span class="k" id="mw_g">else</span><span class="w" id="mw_w"> </span><span class="p" id="mwAQA">{</span> <span class="w" id="mwAQE"> </span><span class="nb" id="mwAQI">Some</span><span class="p" id="mwAQM">(</span><span class="n" id="mwAQQ">x</span><span class="w" id="mwAQU"> </span><span class="o" id="mwAQY">/</span><span class="w" id="mwAQc"> </span><span class="n" id="mwAQg">y</span><span class="p" id="mwAQk">)</span> <span class="w" id="mwAQo"> </span><span class="p" id="mwAQs">}</span> <span class="p" id="mwAQw">}</span> <span class="c1" id="mwAQ0">// divide(1.0, 4.0) -> returns Some(0.25)</span> <span class="c1" id="mwAQ4">// divide(3.0, 0.0) -> returns None</span>

One such way to test whether or not a Maybe contains a value is to use if statements.

<span id="mwARM"></span><span class="kd" id="mwARQ">let</span><span class="w" id="mwARU"> </span><span class="n" id="mwARY">m_x</span><span class="w" id="mwARc"> </span><span class="o" id="mwARg">=</span><span class="w" id="mwARk"> </span><span class="n" id="mwARo">divide</span><span class="p" id="mwARs">(</span><span class="mf" id="mwARw">3.14</span><span class="p" id="mwAR0">,</span><span class="w" id="mwAR4"> </span><span class="mf" id="mwAR8">0.0</span><span class="p" id="mwASA">);</span><span class="w" id="mwASE"> </span><span class="c1" id="mwASI">// see divide function above</span> <span class="c1" id="mwASM">// The if statement extracts x from m_x if m_x is the Just variant of Maybe</span> <span class="k" id="mwASQ">if</span><span class="w" id="mwASU"> </span><span class="kd" id="mwASY">let</span><span class="w" id="mwASc"> </span><span class="nb" id="mwASg">Some</span><span class="p" id="mwASk">(</span><span class="n" id="mwASo">x</span><span class="p" id="mwASs">)</span><span class="w" id="mwASw"> </span><span class="o" id="mwAS0">=</span><span class="w" id="mwAS4"> </span><span class="n" id="mwAS8">m_x</span><span class="w" id="mwATA"> </span><span class="p" id="mwATE">{</span> <span class="w" id="mwATI"> </span><span class="fm" id="mwATM">println!</span><span class="p" id="mwATQ">(</span><span class="s" id="mwATU">"answer: {}"</span><span class="p" id="mwATY">,</span><span class="w" id="mwATc"> </span><span class="n" id="mwATg">x</span><span class="p" id="mwATk">);</span> <span class="p" id="mwATo">}</span><span class="w" id="mwATs"> </span><span class="k" id="mwATw">else</span><span class="w" id="mwAT0"> </span><span class="p" id="mwAT4">{</span> <span class="w" id="mwAT8"> </span><span class="fm" id="mwAUA">println!</span><span class="p" id="mwAUE">(</span><span class="s" id="mwAUI">"division failed, divide by zero error..."</span><span class="p" id="mwAUM">);</span> <span class="p" id="mwAUQ">}</span>

Other languages may have pattern matching

<span id="mwAUk"></span><span class="kd" id="mwAUo">let</span><span class="w" id="mwAUs"> </span><span class="n" id="mwAUw">result</span><span class="w" id="mwAU0"> </span><span class="o" id="mwAU4">=</span><span class="w" id="mwAU8"> </span><span class="n" id="mwAVA">divide</span><span class="p" id="mwAVE">(</span><span class="mf" id="mwAVI">3.0</span><span class="p" id="mwAVM">,</span><span class="w" id="mwAVQ"> </span><span class="mf" id="mwAVU">2.0</span><span class="p" id="mwAVY">);</span> <span class="k" id="mwAVc">match</span><span class="w" id="mwAVg"> </span><span class="n" id="mwAVk">result</span><span class="w" id="mwAVo"> </span><span class="p" id="mwAVs">{</span> <span class="w" id="mwAVw"> </span><span class="nb" id="mwAV0">Some</span><span class="p" id="mwAV4">(</span><span class="n" id="mwAV8">x</span><span class="p" id="mwAWA">)</span><span class="w" id="mwAWE"> </span><span class="o" id="mwAWI">=></span><span class="w" id="mwAWM"> </span><span class="fm" id="mwAWQ">println!</span><span class="p" id="mwAWU">(</span><span class="s" id="mwAWY">"Answer: {}"</span><span class="p" id="mwAWc">,</span><span class="w" id="mwAWg"> </span><span class="n" id="mwAWk">x</span><span class="p" id="mwAWo">),</span> <span class="w" id="mwAWs"> </span><span class="nb" id="mwAWw">None</span><span class="w" id="mwAW0"> </span><span class="o" id="mwAW4">=></span><span class="w" id="mwAW8"> </span><span class="fm" id="mwAXA">println!</span><span class="p" id="mwAXE">(</span><span class="s" id="mwAXI">"division failed; we'll get 'em next time."</span><span class="p" id="mwAXM">),</span> <span class="p" id="mwAXQ">}</span>

Monads can compose functions that return Maybe, putting them together. A concrete example might have one function take in several Maybe parameters, and return a single Maybe whose value is Nothing when any of the parameters are Nothing, as in the following:

<span id="mwAXg"></span><span class="k" id="mwAXk">fn</span><span class="w" id="mwAXo"> </span><span class="nf" id="mwAXs">chainable_division</span><span class="p" id="mwAXw">(</span><span class="n" id="mwAX0">maybe_x</span><span class="p" id="mwAX4">:</span><span class="w" id="mwAX8"> </span><span class="nb" id="mwAYA">Option</span><span class="o" id="mwAYE"><</span><span class="n" id="mwAYI">Decimal</span><span class="o" id="mwAYM">></span><span class="p" id="mwAYQ">,</span><span class="w" id="mwAYU"> </span><span class="n" id="mwAYY">maybe_y</span><span class="p" id="mwAYc">:</span><span class="w" id="mwAYg"> </span><span class="nb" id="mwAYk">Option</span><span class="o" id="mwAYo"><</span><span class="n" id="mwAYs">Decimal</span><span class="o" id="mwAYw">></span><span class="p" id="mwAY0">)</span><span class="w" id="mwAY4"> </span><span class="p" id="mwAY8">-></span><span class="w" id="mwAZA"> </span><span class="nb" id="mwAZE">Option</span><span class="o" id="mwAZI"><</span><span class="n" id="mwAZM">Decimal</span><span class="o" id="mwAZQ">></span><span class="w" id="mwAZU"> </span><span class="p" id="mwAZY">{</span> <span class="w" id="mwAZc"> </span><span class="kd" id="mwAZg">let</span><span class="w" id="mwAZk"> </span><span class="n" id="mwAZo">x</span><span class="w" id="mwAZs"> </span><span class="o" id="mwAZw">=</span><span class="w" id="mwAZ0"> </span><span class="n" id="mwAZ4">maybe_x</span><span class="o" id="mwAZ8">?</span><span class="p" id="mwAaA">;</span> <span class="w" id="mwAaE"> </span><span class="kd" id="mwAaI">let</span><span class="w" id="mwAaM"> </span><span class="n" id="mwAaQ">y</span><span class="w" id="mwAaU"> </span><span class="o" id="mwAaY">=</span><span class="w" id="mwAac"> </span><span class="n" id="mwAag">maybe_y</span><span class="o" id="mwAak">?</span><span class="p" id="mwAao">;</span> <span class="w" id="mwAas"> </span><span class="c1" id="mwAaw">// If both inputs are non-None,</span> <span class="w" id="mwAa0"> </span><span class="c1" id="mwAa4">// check for division by zero and divide accordingly</span> <span class="w" id="mwAa8"> </span><span class="c1" id="mwAbA">// otherwise return None</span> <span class="w" id="mwAbE"> </span><span class="k" id="mwAbI">if</span><span class="w" id="mwAbM"> </span><span class="n" id="mwAbQ">y</span><span class="w" id="mwAbU"> </span><span class="o" id="mwAbY">==</span><span class="w" id="mwAbc"> </span><span class="mi" id="mwAbg">0</span><span class="w" id="mwAbk"> </span><span class="p" id="mwAbo">{</span> <span class="w" id="mwAbs"> </span><span class="nb" id="mwAbw">None</span> <span class="w" id="mwAb0"> </span><span class="p" id="mwAb4">}</span><span class="w" id="mwAb8"> </span><span class="k" id="mwAcA">else</span><span class="w" id="mwAcE"> </span><span class="p" id="mwAcI">{</span> <span class="w" id="mwAcM"> </span><span class="nb" id="mwAcQ">Some</span><span class="p" id="mwAcU">(</span><span class="n" id="mwAcY">x</span><span class="w" id="mwAcc"> </span><span class="o" id="mwAcg">/</span><span class="w" id="mwAck"> </span><span class="n" id="mwAco">y</span><span class="p" id="mwAcs">)</span> <span class="w" id="mwAcw"> </span><span class="p" id="mwAc0">}</span> <span class="p" id="mwAc4">}</span> <span class="n" id="mwAc8">chainable_division</span><span class="p" id="mwAdA">(</span><span class="n" id="mwAdE">chainable_division</span><span class="p" id="mwAdI">(</span><span class="nb" id="mwAdM">Some</span><span class="p" id="mwAdQ">(</span><span class="mf" id="mwAdU">2.0</span><span class="p" id="mwAdY">),</span><span class="w" id="mwAdc"> </span><span class="nb" id="mwAdg">Some</span><span class="p" id="mwAdk">(</span><span class="mf" id="mwAdo">0.0</span><span class="p" id="mwAds">)),</span><span class="w" id="mwAdw"> </span><span class="nb" id="mwAd0">Some</span><span class="p" id="mwAd4">(</span><span class="mf" id="mwAd8">1.0</span><span class="p" id="mwAeA">));</span><span class="w" id="mwAeE"> </span><span class="c1" id="mwAeI">// inside chainable_division fails, outside chainable_division returns None</span>

Instead of repeating Some expressions, we can use something called a bind operator. (also known as "map", "flatmap", or "shove"). This operation takes a monad and a function that returns a monad and runs the function on the inner value of the passed monad, returning the monad from the function.

<span id="mwAe0"></span><span class="c1" id="mwAe4">// Rust example using ".map". maybe_x is passed through 2 functions that return Decimal and String respectively.</span> <span class="c1" id="mwAe8">// As with normal function composition the inputs and outputs of functions feeding into each other should match wrapped types. (i.e. the add_one function should return a Decimal which then can be passed to the decimal_to_string function)</span> <span class="kd" id="mwAfA">let</span><span class="w" id="mwAfE"> </span><span class="n" id="mwAfI">maybe_x</span><span class="p" id="mwAfM">:</span><span class="w" id="mwAfQ"> </span><span class="nb" id="mwAfU">Option</span><span class="o" id="mwAfY"><</span><span class="n" id="mwAfc">Decimal</span><span class="o" id="mwAfg">></span><span class="w" id="mwAfk"> </span><span class="o" id="mwAfo">=</span><span class="w" id="mwAfs"> </span><span class="nb" id="mwAfw">Some</span><span class="p" id="mwAf0">(</span><span class="mf" id="mwAf4">1.0</span><span class="p" id="mwAf8">);</span> <span class="kd" id="mwAgA">let</span><span class="w" id="mwAgE"> </span><span class="n" id="mwAgI">maybe_result</span><span class="w" id="mwAgM"> </span><span class="o" id="mwAgQ">=</span><span class="w" id="mwAgU"> </span><span class="n" id="mwAgY">maybe_x</span><span class="p" id="mwAgc">.</span><span class="n" id="mwAgg">map</span><span class="p" id="mwAgk">(</span><span class="n" id="mwAgo">add_one</span><span class="p" id="mwAgs">).</span><span class="n" id="mwAgw">map</span><span class="p" id="mwAg0">(</span><span class="n" id="mwAg4">decimal_to_string</span><span class="p" id="mwAg8">);</span>

In Haskell, there is an operator bind, or (>>=) that allows for this monadic composition in a more elegant form similar to function composition.

<span id="mwAhs"></span><span class="nf" id="mwAhw">halve</span><span class="w" id="mwAh0"> </span><span class="ow" id="mwAh4">::</span><span class="w" id="mwAh8"> </span><span class="kt" id="mwAiA">Int</span><span class="w" id="mwAiE"> </span><span class="ow" id="mwAiI">-></span><span class="w" id="mwAiM"> </span><span class="kt" id="mwAiQ">Maybe</span><span class="w" id="mwAiU"> </span><span class="kt" id="mwAiY">Int</span> <span class="nf" id="mwAic">halve</span><span class="w" id="mwAig"> </span><span class="n" id="mwAik">x</span> <span class="w" id="mwAio"> </span><span class="o" id="mwAis">|</span><span class="w" id="mwAiw"> </span><span class="n" id="mwAi0">even</span><span class="w" id="mwAi4"> </span><span class="n" id="mwAi8">x</span><span class="w" id="mwAjA"> </span><span class="ow" id="mwAjE">=</span><span class="w" id="mwAjI"> </span><span class="kt" id="mwAjM">Just</span><span class="w" id="mwAjQ"> </span><span class="p" id="mwAjU">(</span><span class="n" id="mwAjY">x</span><span class="w" id="mwAjc"> </span><span class="p" id="mwAjg">`</span><span class="n" id="mwAjk">div</span><span class="p" id="mwAjo">`</span><span class="w" id="mwAjs"> </span><span class="mi" id="mwAjw">2</span><span class="p" id="mwAj0">)</span> <span class="w" id="mwAj4"> </span><span class="o" id="mwAj8">|</span><span class="w" id="mwAkA"> </span><span class="n" id="mwAkE">odd</span><span class="w" id="mwAkI"> </span><span class="n" id="mwAkM">x</span><span class="w" id="mwAkQ"> </span><span class="ow" id="mwAkU">=</span><span class="w" id="mwAkY"> </span><span class="kt" id="mwAkc">Nothing</span> <span class="w" id="mwAkg"> </span><span class="c1" id="mwAkk">-- This code halves x twice. it evaluates to Nothing if x is not a multiple of 4</span> <span class="nf" id="mwAko">halve</span><span class="w" id="mwAks"> </span><span class="n" id="mwAkw">x</span><span class="w" id="mwAk0"> </span><span class="o" id="mwAk4">>>=</span><span class="w" id="mwAk8"> </span><span class="n" id="mwAlA">halve</span>

With >>= available, chainable_division can be expressed much more succinctly with the help of anonymous functions (i.e. lambdas). Notice in the expression below how the two nested lambdas each operate on the wrapped value in the passed Maybe monad using the bind operator.

<span id="mwAl0"></span><span class="w" id="mwAl4"> </span><span class="n" id="mwAl8">chainable_division</span><span class="p" id="mwAmA">(</span><span class="n" id="mwAmE">mx</span><span class="p" id="mwAmI">,</span><span class="n" id="mwAmM">my</span><span class="p" id="mwAmQ">)</span><span class="w" id="mwAmU"> </span><span class="ow" id="mwAmY">=</span><span class="w" id="mwAmc"> </span><span class="n" id="mwAmg">mx</span><span class="w" id="mwAmk"> </span><span class="o" id="mwAmo">>>=</span><span class="w" id="mwAms"> </span><span class="p" id="mwAmw">(</span><span class="w" id="mwAm0"> </span><span class="n" id="mwAm4">λx</span><span class="w" id="mwAm8"> </span><span class="ow" id="mwAnA">-></span><span class="w" id="mwAnE"> </span><span class="n" id="mwAnI">my</span><span class="w" id="mwAnM"> </span><span class="o" id="mwAnQ">>>=</span><span class="w" id="mwAnU"> </span><span class="p" id="mwAnY">(</span><span class="n" id="mwAnc">λy</span><span class="w" id="mwAng"> </span><span class="ow" id="mwAnk">-></span><span class="w" id="mwAno"> </span><span class="kt" id="mwAns">Just</span><span class="w" id="mwAnw"> </span><span class="p" id="mwAn0">(</span><span class="n" id="mwAn4">x</span><span class="w" id="mwAn8"> </span><span class="o" id="mwAoA">/</span><span class="w" id="mwAoE"> </span><span class="n" id="mwAoI">y</span><span class="p" id="mwAoM">))</span><span class="w" id="mwAoQ"> </span><span class="p" id="mwAoU">)</span>

What has been shown so far is basically a monad, but to be more concise, the following is a strict list of qualities necessary for a monad as defined by the following section.

Monadic Type
A type (Maybe)
Unit operation
A type converter (Just(x))
Bind operation
A combinator for monadic functions ( >>= or .flatMap())

These are the 3 things needed to form a monad. Other monads may embody different logical processes, and some may have added properties, but all of them will have these three similar components.

Definition

The more common definition for a monad in functional programming, used in the above example, is actually based on a Kleisli triple ⟨T, η, μ⟩ rather than category theory's standard definition. The two constructs turn out to be mathematically equivalent, however, so either definition will yield a valid monad. Given any well-defined basic types T and U, a monad consists of three parts:

  • A type constructor M that builds up a monadic type M T
  • A type converter, often called unit or return, that embeds an object x in the monad:unit : T → M T
  • A combinator, typically called bind (as in binding a variable) and represented with an infix operator >>= or a method called flatMap, that unwraps a monadic variable, then inserts it into a monadic function/expression, resulting in a new monadic value:(>>=) : (M T, T → M U) → M U so if ma : M T and f : T → M U, then (ma >>= f) : M U

To fully qualify as a monad though, these three parts must also respect a few laws:

  • unit is a left-identity for bind:unit(x) >>= f ↔ f(x)
  • unit is also a right-identity for bind:ma >>= unit ↔ ma
  • bind is essentially associative:ma >>= λx → (f(x) >>= g) ↔ (ma >>= f) >>= g

Algebraically, this means any monad both gives rise to a category (called the Kleisli category) and a monoid in the category of functors (from values to computations), with monadic composition as a binary operator in the monoid and unit as identity in the monoid.

Usage

The value of the monad pattern goes beyond merely condensing code and providing a link to mathematical reasoning. Whatever language or default programming paradigm a developer uses, following the monad pattern brings many of the benefits of purely functional programming. By reifying a specific kind of computation, a monad not only encapsulates the tedious details of that computational pattern, but it does so in a declarative way, improving the code's clarity. As monadic values explicitly represent not only computed values, but computed effects, a monadic expression can be replaced with its value in referentially transparent positions, much like pure expressions can be, allowing for many techniques and optimizations based on rewriting.

Typically, programmers will use bind to chain monadic functions into a sequence, which has led some to describe monads as "programmable semicolons", a reference to how many imperative languages use semicolons to separate statements. However, monads do not actually order computations; even in languages that use them as central features, simpler function composition can arrange steps within a program. A monad's general utility rather lies in simplifying a program's structure and improving separation of concerns through abstraction.

The monad structure can also be seen as a uniquely mathematical and compile time variation on the decorator pattern. Some monads can pass along extra data that is inaccessible to functions, and some even exert finer control over execution, for example only calling a function under certain conditions. Because they let application programmers implement domain logic while offloading boilerplate code onto pre-developed modules, monads can even be considered a tool for aspect-oriented programming.

One other noteworthy use for monads is isolating side-effects, like input/output or mutable state, in otherwise purely functional code. Even purely functional languages can still implement these "impure" computations without monads, via an intricate mix of function composition and continuation-passing style (CPS) in particular. With monads though, much of this scaffolding can be abstracted away, essentially by taking each recurring pattern in CPS code and bundling it into a distinct monad.

If a language does not support monads by default, it is still possible to implement the pattern, often without much difficulty. When translated from category-theory to programming terms, the monad structure is a generic concept and can be defined directly in any language that supports an equivalent feature for bounded polymorphism. A concept's ability to remain agnostic about operational details while working on underlying types is powerful, but the unique features and stringent behavior of monads set them apart from other concepts.

Sign in to keep reading

5 more chapters are free with a Lably account.

More in Computing and communication