Jump to content

Monad (functional programming): Difference between revisions

From Wikipedia, the free encyclopedia
Content deleted Content added
Techniques: Last cleanup on first part of section
Techniques: Settle on final arrangement for second half of section
Line 314: Line 314:


=== Monad concept ===
=== Monad concept ===
Every monad has a specific implementation that defines the required operators and meets the monad laws, but these criteria aren't the only features of a monad. For example, the alternative functions <math>map</math> and <math>join</math> can be automatically derived; many languages have also settled on a standard idiom for features that all monads share.
Every monad has a specific implementation that defines the required operators and meets the monad laws, but these criteria aren't the only features of a monad.
For example, functionals like <math>map</math> and <math>join</math> can be automatically derived; many languages have also settled on a standard idiom for features that all monads share.


For these reasons, it makes sense for a language to provide a [[concept (generic programming)|generic concept]] as a template to build specific monads from. This also guarantees that when a programmer creates a new monad, it still inherits any relevant features from superclasses like functors.
As a result, it makes sense for a language to provide a [[concept (generic programming)|generic concept]] or template to build specific monads from.
Besides simplifying development and guaranteeing a new monad inherits relevant features from superclasses (such as functors), matching a specification adds another layer of quality control.


For example, Haskell provides a general monad class in its base libraries.
For example, Haskell provides a general monad class in its base libraries. Not only does the type class derive from an applicative functor class, but several useful functions and operators are pre-defined in terms of the <math>bind</math> operator, which the type class still provides a type signature to ensure type safety.<ref name="HaskellBase">{{cite web | url = https://hackage.haskell.org/package/base | title = (Haskell) base: Basic libraries | accessdate = 7 October 2018}}</ref> Other languages might use different mechanisms to provide concepts, but however a language provides it, a rigorous template for the monad pattern makes development more convenient and improves the quality of the resulting software.
Not only does the type class derive from an applicative functor class, but several useful functions and operators are pre-defined in terms of the <math>bind</math> functional, plus the type class provides [[type signature]]s to improve [[type safety]].<ref name="HaskellBaseMonad" />


=== Bonus functionals <span id="Lift"></span><span id="Compose"></span> ===
=== Bonus functionals <span id="Lift"></span><span id="Compose"></span> ===
On top of the [[higher-order function]]s already mentioned, several other handy ones can be defined for the general monad concept.
On top of the functionals already mentioned, several other handy ones can be defined for monads in general.


One inconvenience of the monad pattern that might have already come to mind is redefining functions to output monadic types. Fortunately, so long as a monadic function acts fundamentally the same on underlying values, this can be done generically, with the functional <math>liftM</math>, which "[[lift (mathematics)|lift]]s" a function or operator into a monadic version. In some languages, multiple versions of <math>liftM</math> may exist depending on how many parameters a function takes, but we will assume that is automatic here.
One inconvenience of the monad pattern that might have already come to mind is redefining functions to output monadic types.
Fortunately, so long as a monadic function operates on the underlying values the same as some simpler function, it can be defined generically with '''lift'''.
It essentially does what it says, "[[lift (mathematics)|lift]]ing" a function or operator from a basic version to a monadic one.{{efn|In some languages, multiple versions of <math>lift</math> may exist depending on how many parameters a function takes, but we will assume that is managed automatically here.}}


To see <math>liftM</math> in action, we can return to the <code>add</code> function of the previous examples. We have already simplified its definition from CPS code to monadic code, then yet again using imperative syntax sugar. With <math>liftM</math>, however, we can lift the semantics of basic addition and compress it further still, into simply:
To see <math>lift</math> in action, consider the <code>add</code> function for <code>Maybe</code> numbers once more. However much using <math>bind</math> or do-notation simplified the definition, <math>lift</math> can distill it even further into the very concise:
<source>add (mx, my) = liftM(+)</source>
add(mx, my) = lift(+)


Even this is not fully simplified though; since <math>liftM</math> is a capability of monads ''in general'', we can define naive monadic addition at once for all monads that satisfy a <code>Monad</code> class, not just <code>Maybe</code>. We don't even need to tweak the above definition, just generalize the type signature for <code>add</code>:
Even this is not the full power of <math>lift</math> though; since it is a capability of monads in general, this definition can work for ''all'' monads that satisfy the <code>Monad</code> class specification, not just <code>Maybe</code>.
The above definition doesn't even need changing, only the type signature for <code>add</code> needs to be generalized:
<source>add : (Monad Number, Monad Number) -> Monad Number</source>
add : (Monad Number, Monad Number) Monad Number


Another higher-order operator, which can be useful in analysis just as much as applications, is monadic function composition. While this article assumes variable binding is automatic, in languages where this is not the case, monadic composition can be used to sidestep it. For example, here is a definition of a monadic composition operator <math>>=></math> along with a third form of the monad laws:
Another higher-order operator, which can be useful in analysis just as much as applications, is monadic function composition.
This operator allows chaining functions even more naturally, especially if variable binding must be done explicitly.
<source>
For example, if the symbol <code>>=></code> is used for the composition operator, the monad laws can be converted to an especially insightful form:
(f >=> g) x = f(x) >>= g
(f >=> g) x = (f(x) = my) >>= g(y)


(unit >=> g) == g
(unit >=> g) == g
(f >=> unit) == f
(f >=> unit) == f
(f >=> g) >=> h == f >=> (g >=> h)
(f >=> g) >=> h == f >=> (g >=> h)
</source>


This alternate formulation of the laws makes it especially clear that they come down to two-sided identity and associativity. Algebraically, this means that any monad is ''also'' a [[monoid]] over functors (from values to computations), with monadic composition as the binary operator and <math>unit</math> its identity.
This version of the laws makes it very clear that they amount to associative composition and <math>unit</math> being a two-sided identity.{{efn|Algebraically, this means that any monad is both a category in its own right ''and'' a [[monoid]] over functors (from values to computations), with composition as the monoidal binary operator and <math>unit</math> the identity.}}


== Variations ==
== Variations ==

Revision as of 22:18, 5 November 2018

In functional programming, a monad is a design pattern[1] that can structure program logic in a way that is both generic and declarative. More specifically, a monad generically transforms underlying data types into richer types with some standard higher-order functions (also called "functionals") that must obey formal rules known as the monad laws.[2] So long as these criteria are met, monads can represent many kinds of computations at a high level of abstraction, not just pure functions.[3]

This allows monads to simplify a wide range of problems elegantly, keeping the many benefits of purely functional programming like referential transparency, but without any need to extend the language's main semantics. With monads, a programmer can chain functions in logical order (independent of time and evaluation strategy) to build a succinct pipeline, which handles additional data, control flow, or side-effects automatically.[4][5]

Some languages, particularly functional ones like Haskell, offer libraries with definitions for a general Monad class and often-used instances.[6] However, since monads are ultimately an abstract pattern, one can implement them conveniently in any language that supports parametric polymorphism.[citation needed]

Overview

The idea of a monad originally comes from category theory, where it is defined as an endofunctor with additional structure.[a][3] When translated into functional programming terms, a monad acts as a generic concept (or an equivalent feature like a type class), applying structure and features to simpler types generically instead of relying on their underlying semantics. Working on types yet remaining agnostic about their operational details gives monads much of their expressive power; what sets monads apart from other generics though are their stringent behavior and unique functionals.[7]

The monad pattern does not arise as often in object-oriented programming, but many object-oriented languages offer mechanisms similar to type classes, generics in Java or templates in C++ for instance. One can use these features to implement and deploy monads even in languages that are not normally considered functional.[citation needed]

Conceptual definition

Altogether, a monad contains three parts:

  1. A type constructor that specifies how the monadic type is built up from other pre-existing types.
  2. A type converter, often called unit or return, that wraps a single variable in the monad.
  3. A combinator, typically called bind (as in binding a variable), that unwraps a monadic variable, inserts it into an expression, then rewraps it all in the monad.

To fully qualify as a monad, these three parts must also respect a few laws, which the programmer should check (and ideally prove) independently.[2] These laws intuitively come down to...

  1. Converting a variable with only to immediately it to a function should be redundant and no different from just applying the function to the original variable.
  2. Using to feed an already monadic variable to itself is also redundant and should have no effect.
  3. When is used repeatedly to chain functions, the order of evaluation for ifself should be irrelevant.[b]

The upshot is that when all these criteria are satisfied, a monad can reify any computation characterized by its type constructor. This in turn allows structuring a program concisely, even with side-effects, while keeping the rigor and flexibility of purely functional programming.[5][8]

Utility

The monad pattern has proven valuable for combining abstract usability with functional precision in many diverse scenarios, not limited to:[citation needed]

Oftentimes, programmers will use to chain functions on the monad 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.[2] Monads, however, do not actually order program flow so much as encapsulate additional logic that interlaces with other formalisms (like currying). 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. Their general utility lies in simplifying a program's structure and improving separation of concerns through abstraction.[5][9]

Especially in purely functional programming, functions cannot rely on state without complex workarounds, nor should order of evaluation matter. Only explicit function composition should impose a sequence on computations. Functional languages can still express these other situations, including impure computations and strict evaluation, through a complex scaffold of function composition and continuation-passing style (CPS).[4] With monads though, much of this scaffolding can be abstracted away, essentially by bundling each recurring pattern from the CPS code into a distinct monad.[5] Since this lets application programmers implement domain logic while offloading bookkeeping onto pre-developed modules, monads can even be considered a tool for aspect-oriented programming.[10]

An example: Maybe

A very common issue for robust programs is preparing for undefined operations or variables. This turns out to be a very natural application for a monad, specifically one known as the Maybe monad.[2]

To start, an option type Maybe can be defined to contain either a value, of any type, or an undefined value Nothing. The word Just will also mark a plain value embedded in Maybe to avoid any confusion with unembedded values:

newtype Maybe T  =  Just T or Nothing

We would like to use this type as a simple checked exception: if the computation becomes undefined at any point, it will short-circuit and return Nothing. If all steps of the calculation succeed though, the final result should just be the correct value.

Consider an addition function add that should do exactly this when adding two Maybe variables, mx and my. Using a naive approach to define the function for all cases will lead to a tedious series of conditionals though:

add : (Maybe Number, Maybe Number)  →  Maybe Number
add(mx, my)  =  ...
    if mx is Nothing then
        ... Nothing
    else if my is Nothing then
        ... Nothing
    else
        ... Just(x + y)
    end if

To alleviate this, we can define an operation for chaining the steps together; we'll use the infix symbol >>= and define it to feed a (possibly undefined) result from each step into the next. Since we are technically inserting the result into another function, we will take a function as input to our operator. The add function already fixes down the underlying types for us too so we may as well allow this operator to output a different type from the input:

>>= : (Maybe T, (T → Maybe U))  →  Maybe U
(ma >>= f)  =  ...
    if ma is (Just a) then
        ... f(a)
    else
        ... Nothing
    end if

We can now redefine add as something much more compact:

add(mx, my)  =  mx >>= (my >>= Just(x + y))

This is more elegant, but a little extra analysis reveals something far more powerful. For one, notice that within either version of add, Just is only needed to tag a plain value as now a Maybe value. To emphasize how Just merely wraps existing values, we can treat it as a function too, which we'll call eta for now:

eta : T  →  Maybe T
eta(x)  =  Just x

The key insight is that now we have two functions, >>= and eta, which greatly simplified add but without any specific dependence on its semantics whatsoever. We can, in fact, apply these functions to simplify any code that acts on Maybe, even if the underlying types are entirely different. For example, here is a concise NOT operator from (Kleene's) trinary logic that completely automates undefined values:

trinot : Maybe Boolean  →  Maybe Boolean
trinot(mp)  =  mp >>= eta(not(p))

This Maybe type along with its two functionals is a monad, >>= acting as and eta as . Other monads will embody different logical processes, and some monads have extra properties that can make them more powerful, but all of them follow the basic outline of this example.

History

The term "monad" in programming actually goes all the way back to the APL and J programming languages, which do tend toward being purely functional. However, in those languages, "monad" was simply shorthand for a function taking one parameter (a function with two parameters being a "dyad", and so on).[citation needed] This usage predates the first discussions of programming with true monads by decades and is roughly contemporaneous with the first work on monads in category theory.[citation needed]

The mathematician Roger Godement was the first to formulate the concept of a monad in the late 1950s. Over the next decades, as theorists developed the idea further, the term "monad" due to mathematician and leading category-theorist Saunders Mac Lane came to dominate.[citation needed]

Starting in the 1980s, a vague notion of a monad pattern began to surface in the computer science community. According to programming language researcher Philip Wadler, computer scientist John C. Reynolds anticipated several facets of it in the 70s and early 80s; Reynolds had discussed the value of continuation-passing style, category theory as a rich source for program semantics, and the type distinction between values and computations.[5] The research language Opal, which was actively designed up until 1990, also effectively based I/O on a monadic type although the connection was not realized at the time.[citation needed]

The computer scientist Eugenio Moggi was the first to make an explicit link between the monad of category theory and functional programming, in a conference paper in 1989,[11] followed by a more refined journal submission in 1991. In earlier work, several computer scientists had advanced the idea of using category theory to provide general semantics for the lambda calculus. Moggi's key insight was that a real-world program is not just a function that maps values to other values, but rather a transformation that forms computations on those values. When formalized in category-theoretic terms, this leads to the conclusion that a monad is the structure to represent these computations.[3]

Several others popularized and built on this idea, including Philip Wadler[4] and Simon Peyton Jones, both of whom were involved in the specification of Haskell. In particular, early versions of Haskell used a problematic "lazy list" model for I/O, and Haskell 1.3 introduced a monadic interface as a more flexible way to combine I/O with lazy evaluation.[citation needed] Besides I/O, the Haskell community successfully applied monads to a wide range of design problems, including imperative syntax sugar (known in Haskell as "do-notation"). Researchers working with Haskell also eventually generalized the monad pattern into a wider hierarchy of structures, including applicative functors and arrows.[citation needed]

At first, programming with monads was largely confined to Haskell and its derivatives, but as functional programming has influenced other paradigms, many languages have incorporated a monad pattern (in spirit if not in name). Formulations now exist in Scheme, Perl, Python, Racket, Clojure, Scala, F#, and have also been considered for a new ML standard.[citation needed]

Formal definition

Since monads are ultimately mathematical objects, working from a formal definition can dodge some subtler pitfalls and inject more rigor into a program. Within computer science, a couple different definitions have become standard, and though one or the other may be more convenient in a given situation, they are both equivalent. Whichever one a programmer chooses, the formal definition allows verifying a program's logic and in some cases, optimizing code through rewriting.[citation needed]

Kleisli triple

The more common definition in functional programming, used in the Maybe example, actually differs from the standard definition of category-theory. Instead, the package of type constructor, , and makes up what is known as a Kleisli triple. These three components turn out to characterize a specific monad anyways though, so the triple can act as an alternate definition.[citation needed]

To formalize the concept, we will first pin down the three components. For any well-defined types T,U:

  • The type constructor M marks a new higher-order type M T[c]
  • defines a specific procedure to embed any object a of type T into a monad with type M T:
unit(a) : T → M T[d]
  • (expressed as >>=) defines how to insert any monadic object ma of type M T (including a first-class function with that output) into any function f : T → M U:
(ma >>= f) : (M T, (T → M U))  →  M U[e]

If this seems like unnecessary hair-splitting, the added value comes from now formalizing the monad laws with the same symbols:[12]

unit(a) >>= f(x)  ==  f(a)
ma >>= unit(x)    ==  ma
mx >>= (f(x) = my >>= g(y) = mz)  ==  (mx >>= f(x) = my) >>= g(y) = mz

Now that we have a solid foundation to start from, we can analyze monads with much more precision.

Verifying the monad laws

Returning to the Maybe example, while its components were matched to the required parts of a monad, there was no demonstrative proof it satisfies the monad laws. Now with a formal definition, this can be rectified:

Law 1:  eta(a) >>= f(x)  ==  (Just a) >>= f(x)
        ==  f(a)
Law 2:  ma >>= eta(x)  ==  Nothing >>= eta(x)               ==  Nothing ...
    or                 ==  (Just a) >>= eta(x)  ==  eta(a)  ==  Just a
        ==  ma
Law 3:  (ma >>= f(x)) >>= g(y)  ==  (Nothing) >>= g                           ==  Nothing ...
    or                          ==  f(a) = my >>= g(y)  ==  Nothing >>= g(y)  ==  Nothing ...
    or                                                  ==  g ∘ f(a)          ==  ma >>= (g ∘ f(x))
        ==  ma >>= (f(x) = my >>= g(y))

Note how the proof procedure takes the specifics of Maybe, then applies them as rewrite-rules to one side of each law's equation. The proof succeeds if an algebraic chain of equalities can reach the other side of the law in question.[citation needed]

Derivation from functors

Although computer scientists usually define monads by way of Kleisli triples, one can also use the standard definition from category theory as a recipe. Mathematically, a monad is actually just a functor with two additional natural transformations, one of which is the functional already described. As a result, this approach only differs from the Kleisli triple by replacing with the other transformation.

A second functional map is also necessary, but this is typically not a major inconvenience since it is defined for all functors; if a monad is derived from a functor instead of defined from scratch, is inherited automatically from the parent functor. Details can be found in the article for the map functional, but one key point is that can act on any mapping, not just functions with monadic output like :

map(h) : (a → b)  →  (ma → mb)

If the class hierarchy is followed closely, it turns out isn't truly unique to monads either, but rather to applicative functors, an intermediate structure between a monad and a vanilla functor. In the applicative context, is sometimes referred to as pure, but is effectively the same operator. The only effective difference from the derivation based on Kleisli triples is that without , must respect a different law in terms of :[13]

unit ∘ h(x)  ==  (map h) ∘ unit(x)

The real leap from applicative functor to monad comes with the join functional, which does characterize the resulting monad:

  • allows "flattening" repeated applications of the monadic functor:[14]
join(mma) : M (M T) → M T

As the characteristic functional, must also satisfy three variations on the monad laws:

join ∘ (map join) mmma    ==  join ∘ join(mmma)    ==  ma
join ∘ (map unit) ma      ==  join ∘ unit(ma)      ==  ma  
join ∘ (map ∘ map h) mma  ==  (map h) ∘ join(mma)  ==  mb

A developer may pick either way to define a monad, but the results are mathematically the same, and if necessary, deriving components of one form from the other is very straight-forward:[citation needed]

(map h) ma   ==  ma >>= (unit ∘ h(x))
join(mma)    ==  mma >>= id(x)
ma >>= f(x)  ==  join ∘ (map f) ma

Another example: List

The complex multivalued square and cube root functions may be composed so they produce the sixth root function. The structure that governs the type of input and output and the structure that composes the different operations are, together, a list monad.[15]

A monad that demonstrates the alternative definition's usefulness, and how diverse monads are, is the List monad; yes, the standard list structure does in fact become a monad so long as we provide the necessary parts. With a basic operator ++, the type constructor can follow the typical recursive definition:

List T  =  []  or  T ++ (List T)

Embedding a plain value in a list is also trivial in most languages:

unit(x)  =  [x]

At first glance, applying a function iteratively with a list comprehension may seem like an easy choice for too. However, this solution won't work as-is because expects functions with monadic output, i.e. more (possibly non-trivial) lists. To maintain a true list, must flatten any resulting nested structure after iterating over the input. Thankfully, these two tasks match up with and :

(map h) xlist    =  [ h(x1), h(x2), ..., h(xn) ]
join(xlistlist)  =  xlist1 ++ xlist2 ++ ... ++ xlistn

Now with these functionals defined, comes from the equivalence formulas automatically:

(xlist >>= f)  =  join ∘ (map f) xlist

One scenario for this monadic list is representing nondeterministic computation. List can hold results from each step in an algorithm for all execution paths, then condense itself to "forget" which paths led to which results (a sometimes important distinction from deterministic, exhaustive algorithms). Another benefit is that checks can be included in List's definition, stopping and removing paths at their first point of failure, without any changes to functions in the pipeline. In languages with lazy evaluation, list elements are only generated as needed, which can also notably improve efficiency.[citation needed]

A second situation where List shines is composing multivalued functions. For instance, consider the complex roots of a number: the nth root of a number will return n distinct complex numbers, but if another mth root of those results is taken, the final m•n values should be identical to the output of the m•nth root function. List completely automates this issue away, condensing the results from each step into a flat, mathematically correct list.[15]

Techniques

Monads present opportunities for some other interesting techniques beyond just organizing program logic. Since monads preserve referential transparency, they can lay the groundwork for useful syntactic features while their abstract and mathematical nature enable some very powerful higher-order functions.

Syntax Sugar

Although it often makes sense to use directly, many programmers use a special format that mimics imperative statements (called do-notation in Haskell, perform-notation in OCaml, computation expressions in F#,[16] and for comprehension in Scala). This is only syntactic sugar that allows writing sequential statements within a code block; the compiler will then quietly translate these expressions into more functional, monadic code.

To see this feature in action, consider the add function from the Maybe example. If we wanted to implement the non-monadic version in Haskell, it would be:

add mx my =
    case mx of
        Nothing -> Nothing
        Just x  -> case my of
                       Nothing -> Nothing
                       Just y  -> Just (x + y)

In monadic Haskell, return is the standard name for , plus lambda expressions must be handled explicitly. Even with these technicalities, the Maybe monad still makes for a cleaner definition:

add mx my =
    mx >>= (\x ->
        my >>= (\y ->
            return (x + y)))

With do-notation though, we can distill this even further into a very intuitive sequence:

add mx my = do
    x <- mx
    y <- my
    return (x + y)

Here is a second example that also uses Maybe but in an entirely different language: F#. It implements a "safe division" function (returning None if either operand is undefined or division by zero occurs) using computation expressions:

let readNum () =
  let s = Console.ReadLine()
  let succ,v = Int32.TryParse(s)
  if (succ) then Some(v) else None

let secure_div = 
  maybe { 
    let! x = readNum()
    let! y = readNum()
    if (y = 0) 
    then None
    else return (x / y)
  }

At build-time, the compiler will internally "de-sugar" this function into a denser chain of calls:

maybe.Delay(fun () ->
  maybe.Bind(readNum(), fun x ->
    maybe.Bind(readNum(), fun y ->
      if (y=0) then None else maybe.Return(x / y))))

For a last example, note that even the general monad laws themselves can be expressed in do-notation:

do { x <- return v; f x }            ==  do { f v }
do { x <- m; return x }              ==  do { m }
do { y <- do { x <- m; f x }; g y }  ==  do { x <- m; y <- f x; g y }

Exactly how various languages sugarcoat monads are beyond the scope of this article, but a developer should always remember that it is purely a syntactic convenience. Imperative-style blocks can always be replaced with direct monadic (or even non-monadic CPS) expressions. While these blocks are convenient in many situations, such as assigning/binding several variables (which would otherwise require a great deal of nesting), can be clearer and shorter just as often. Some functional programming advocates even argue that since this syntax sugar allows novices to carry over poor habits and assumptions from imperative programming, it should be avoided by default and only used when obviously superior.[17][2]

Monad concept

Every monad has a specific implementation that defines the required operators and meets the monad laws, but these criteria aren't the only features of a monad. For example, functionals like and can be automatically derived; many languages have also settled on a standard idiom for features that all monads share.

As a result, it makes sense for a language to provide a generic concept or template to build specific monads from. Besides simplifying development and guaranteeing a new monad inherits relevant features from superclasses (such as functors), matching a specification adds another layer of quality control.

For example, Haskell provides a general monad class in its base libraries. Not only does the type class derive from an applicative functor class, but several useful functions and operators are pre-defined in terms of the functional, plus the type class provides type signatures to improve type safety.[6]

Bonus functionals

On top of the functionals already mentioned, several other handy ones can be defined for monads in general.

One inconvenience of the monad pattern that might have already come to mind is redefining functions to output monadic types. Fortunately, so long as a monadic function operates on the underlying values the same as some simpler function, it can be defined generically with lift. It essentially does what it says, "lifting" a function or operator from a basic version to a monadic one.[f]

To see in action, consider the add function for Maybe numbers once more. However much using or do-notation simplified the definition, can distill it even further into the very concise:

add(mx, my)  =  lift(+)

Even this is not the full power of though; since it is a capability of monads in general, this definition can work for all monads that satisfy the Monad class specification, not just Maybe. The above definition doesn't even need changing, only the type signature for add needs to be generalized:

add : (Monad Number, Monad Number)  →  Monad Number

Another higher-order operator, which can be useful in analysis just as much as applications, is monadic function composition. This operator allows chaining functions even more naturally, especially if variable binding must be done explicitly. For example, if the symbol >=> is used for the composition operator, the monad laws can be converted to an especially insightful form:

(f >=> g) x  =  (f(x) = my) >>= g(y)
(unit >=> g)     ==  g
(f >=> unit)     ==  f
(f >=> g) >=> h  ==  f >=> (g >=> h)

This version of the laws makes it very clear that they amount to associative composition and being a two-sided identity.[g]

Variations

At a purely mathematical level, some monads have nice properties that make them even more powerful. This section includes a few significant cases.

Additive monads

An additive monad is a monad endowed with a closed, associative, binary operator mplus and an identity element under , called mzero. In other words, an additive monad also qualifies as a monoid in regards to .

The reason for calling the identity as a "zero", rather than a "one", is that does act as an absorber for . Just as multiplying any number by 0 results in 0, binding with any function yields . This property is two-sided, also coming into play if one binds a non-empty monad to a zero function.[h]

Intuitively, represents a monadic wrapper with no value from an underlying type. So the Maybe monad can be considered additive, with Nothing as and a variation on the OR operator as . List is also an additive monad, with the empty list [] acting as and the concatenation operator ++ as

The free monad

In some situations, the general features of a monad may be useful but there is no specific pattern to recommend one kind or another. This is where the free monad comes in. The free monad is unique because it doesn't implement any particular semantics like other monads. Instead, it is a free object in the class of monads, an entirely formal object that satisfies the laws of its class without any evaluation or deeper assigned meaning.

Perhaps the most direct explanation for the free monad begins from the fact that all monads are monoids over functors. Just as a free monoid simply concatenates elements into a string without evaluation, the free monad provides a handle to compose and navigate chains of functors.[i]

In code, this means a free monadic type expresses nested functors with the bare minimum needed to satisfy the type system. For example, a recursive and linear definition for a free monad might look like this, with type markers standing in for and :

new type (Free F(T))  =  Unit T or Bind (Free G(U), F)

At first, this may seem like a solution in search of a problem, but many syntactic operations suit the free monad's formal nature. Whenever syntax and type are processed in separate phases, a free monad can work on computational chains while leaving semantics for later. As a result, it is often used in parsers and interpreters for domain-specific languages, and others have applied them to more dynamic problems, such as iteratees.[citation needed]

Comonads

Besides generating monads with extra properties, for any given monad, one can also define a comonad. This is just a categorical dual, which loosely means that it will have the same required components, only with the direction of the type signatures reversed. So if we start from the -centric definition of a monad, a comonad consists of:

  • A type constructor W that marks the higher-order type W T
  • An inversion of that extracts the underlying value from the comonad:
extract (wa) : W T -> T
  • A reversal of that extends a chain of reducing functions on comonadic values:
extend (wa,f) : (W U, W U -> T) -> W T

The and functionals must also satisfy duals of the monad laws:

extract ∘ extend (wa,f)    ==  f(wa)
extend (wa, extract)       ==  wa
extend (extend (wa,f), g)  ==  extend (wa, λx.extend (f(x), g))

Analogous to monads, comonads can also be derived using a dual of :[j]

  • takes an already comonadic value and wraps it in another layer of comonadic structure:
duplicate (wa) : W T -> W (W T)

A definition by , , and must also respect duals of the alternative laws:

(map duplicate) ∘ duplicate  ==  duplicate ∘ duplicate
(map extract) ∘ duplicate    ==  extract ∘ duplicate    ==  id
((map ∘ map) f) ∘ duplicate  ==  duplicate ∘ (map f)

And as with monads, the two sets of functionals can be converted automatically:

(map f) wwa     ==  extend (wwa, f ∘ extract)
duplicate wb    ==  extend (wb, id)

extend (wc, g)  ==  (map g) ∘ duplicate(wc)

Conceptually, if monads represent computations built up from underlying values, then comonads can be seen as reductions back down to values. One limitation of monadic code is that it can't, in a sense, be fully "unpacked"; once a first-order value is embedded into a monad, it remains quarantined inside the monad along with any side-effects (a good thing in purely functional programming). Sometimes though, a specific problem or paradigm is really more concerned with consuming contextual data to yield basic values. It turns out that comonads are ideal for modeling this logic in explicit code.[citation needed]

One simple example is the product comonad, which outputs values based on an input value and shared environment data.[k] A less trivial example is the stream comonad, which is essentially dual to List. Rather than emphasizing concatenation, however, Stream uses to consume elements from the front of the queue. Perhaps the real power of Stream comes from using though, where each comonadic function filters the incoming data stream. In fact, while not as popular as monads, researchers have found comonads particularly useful for processing data streams and modeling the semantics of dataflow programming languages.[citation needed]

Since monads and comonads are both mathematically strict, one cannot simply move objects back and forth between the two. As an even higher abstraction, (Hughes) arrows can encompass both structures, but finding more concrete ways to combine monadic and comonadic code is an active area of research.[citation needed]

More examples

Identity monad

The simplest monad is the identity monad, which attaches no information to values but still satisfies the monad laws:[l]

new type Id T  =  T

unit(x)    =  x
(x >>= f)  =  f(x)

The Identity monad does actually have valid uses though, such as providing a base case for recursive monad transformers. It can also be used to perform basic variable substitution within an imperative-style block.[citation needed]

Collections

As we saw above, a list is actually a monad too, particularly since it has a well-defined procedure for flattening nested structure. It turns out this is true for several familiar collection types; in fact, while every monad is technically a monoid, collections with a concatenation operator are free monoids.[m]

By picking special properties under concatenation, we can even characterize a specific collection:

Collection Monoid properties
List Free
Finite multiset Commutative
Finite set Commutative & idempotent
Finite permutations Non-commutative & idempotent

I/O monad (Haskell)

As already mentioned, functions should not have any unmanaged side effects in pure code, but that does not exclude a function from explicitly describing an effect, then passing this handle through the program for later use. This idea is at the heart of Haskell's IO monad, where an object of type IO can be seen as containing the current state of the world outside the program. When a programmer binds an IO monad to a function, the function can make decisions based on a view of the world (user or system input), then yield a new world-state as a monadic value (program output).[18]

For example, Haskell has several functions for acting on the wider file system, including one that checks whether a file exists and another that deletes a file. Their two type signatures are:

doesFileExist :: FilePath -> IO Bool
removeFile :: FilePath -> IO ()

Since the first is interested in whether a given file actually exists, a true or false question about the outside world, it embeds a boolean value within the IO monad. The second function, on the other hand, is only concerned with the action on the file system. As a result, it wraps an undefined value in the IO structure because only the outside action is relevant.

The IO monad does not restrict us to file I/O though; we can even use it to mimic a typical "Hello, World!" program like in imperative languages:

main :: IO ()
main = do
  putStrLn "Hello, world!"
  putStrLn "What is your name, user?"
  name <- getLine
  putStrLn ("Nice to meet you, " ++ name ++ "!")

Desugared, this translates into the following monadic pipeline:[n]

main :: IO ()
main =
  putStrLn "Hello, world!" >>
  putStrLn "What is your name, user?" >> 
  getLine >>= (\name ->
    putStrLn ("Nice to meet you, " ++ name ++ "!"))

Writer monad (Javascript)

Another common situation is keeping a log file or otherwise updating the user about a program's progress. Sometimes, a programmer may want to log even more specific, technical data for later profiling or debugging. The writer monad can handle these tasks by generating auxiliary output that accumulates step-by-step.

To show how the monad pattern isn't restricted to primarily functional languages, we can implement and use a Writer monad in Javascript. First, we will use an array (with nested tails) to construct our Writer type as a linked list. The underlying output value will live in position 0 of the array, and position 1 will implicitly hold a chain of auxiliary notes:

const writer = [value, []];

Defining is also very simple:

const unit = value => [value, []];

Now that we have settled on how to embed values into the monadic type, we can define some sample functions that will output Writer objects with debugging notes:

const squared = x => [x * x, [`${x} was squared.`]];
const halved = x => [x / 2, [`${x} was halved.`]];

To have a true monad though, we still need to define , but in this case, this is just a matter of properly appending a function's results into a Writer object's linked list:

const bind = (writer, transform) => {
    const [value, log] = writer;
    const [result, updates] = transform(value);
    return [result, log.concat(updates)];
};

With this done, the sample functions can now be chained together using . However, by defining a version of monadic composition (we will call it pipelog), we can apply our functions in an even more succinct notation:

const pipelog = (writer, ...transforms) =>
    transforms.reduce(bind, writer);

The final result is a clean separation of concerns between stepping through the computations and logging them to audit later on:

pipelog(unit(4), squared, halved);
// Resulting writer object = [8, ['4 was squared.', '16 was halved.']]

Environment monad

An environment monad (also called a reader monad and a function monad) allows a computation to depend on values from a shared environment. The monad type constructor maps a type T to functions of type ET, where E is the type of the shared environment. The monad functions are:

The following monadic operations are useful:

The ask operation is used to retrieve the current context, while local executes a computation in a modified subcontext. As in a state monad, computations in the environment monad may be invoked by simply providing an environment value and applying it to an instance of the monad.

State monads

A state monad allows a programmer to attach state information of any type to a calculation. Given any value type, the corresponding type in the state monad is a function which accepts a state, then outputs a new state (of type s) along with a return value (of type t). This is similar to an environment monad, except that it also return a new state, and thus allows modeling a mutable environment.

type State s t = s -> (t, s)

Note that this monad, unlike those already seen, takes a type parameter, the type of the state information. The monad operations are defined as follows:

-- "return" produces the given value without changing the state.
return x = \s -> (x, s)
-- "bind" modifies m so that it applies f to its result.
m >>= f = \r -> let (x, s) = m r in (f x) s

Useful state operations include:

get = \s -> (s, s) -- Examine the state at this point in the computation.
put s = \_ -> ((), s) -- Replace the state.
modify f = \s -> ((), f s) -- Update the state

Another operation applies a state monad to a given initial state:

runState :: State s a -> s -> (a, s)
runState t s = t s

do-blocks in a state monad are sequences of operations that can examine and update the state data.

Informally, a state monad of state type S maps the type of return values T into functions of type , where S is the underlying state. The return and bind function are:

.

From the category theory point of view, a state monad is derived from the adjunction between the product functor and the exponential functor, which exists in any cartesian closed category by definition.

Continuation monad

A continuation monad with return type maps type into functions of type . It is used to model continuation-passing style. The return and bind functions are as follows:

The call-with-current-continuation function is defined as follows:

See also

  • Arrows in functional programming – whereas monads generalize the results of a computation to effects, arrows further generalize the inputs similarly
  • Aspect-oriented programming – a paradigm to increase modularity by isolating secondary or supporting functionality
  • Effect system – an alternative way of describing side effects as types
  • Inversion of control – the abstract principle of calling specific functions from a reusable software entity
  • Monad transformers – which allow monads to be composed in a modular and convenient way
  • Uniqueness types - an alternative way of dealing with side-effects in functional languages
  • xmonad – an Xwindows tiling windows manager programmed in Haskell using a monad design pattern

Notes

  1. ^ Due to the fact that functions on multiple free variables are common in programming, "monads" as described in this article are technically what category theorists would call "strong monads".
  2. ^ Technically, is strictly associative if the functions involved are given as lambda expressions. This is because, if one is being precise, corresponds to abstraction within the lambda calculus, a distinct rule from function application.
  3. ^ Semantically, is not trivial and represents an endofunctor over the category of all well-typed values: . This is how a monad reifies computations on values, but outside of theoretical work, probably isn't a critical distinction.
  4. ^ While a "function" in programming terms, (often called in category-theory) is mathematically a natural transformation, which maps between functors:
  5. ^ , on the other hand, is not a natural transformation but an extension that lifts a mapping (from values to computations) into a morphism between computations:
  6. ^ In some languages, multiple versions of may exist depending on how many parameters a function takes, but we will assume that is managed automatically here.
  7. ^ Algebraically, this means that any monad is both a category in its own right and a monoid over functors (from values to computations), with composition as the monoidal binary operator and the identity.
  8. ^ Combined with the fact that every monad is also a monoid under monadic composition (), if is commutative and distributes over it, the additive monad even qualifies as a semiring.
  9. ^ The need to respect composition does not necessarily restrict a free monad to a list structure; one can be defined to keep functors in other structures, such as trees.
  10. ^ While operations like are themselves reversed, they don't reverse functions they act on. So comonads are still functors, not cofunctors, and is the same for comonads.
  11. ^ The Product comonad is actually the dual of the Writer monad described below; furthermore, it is effectively the same as the Reader monad, only differing in how the (co)monad and functions alternate to pass on the environment.
  12. ^ In category theory, the Identity monad can also be viewed as emerging from adjunction of a functor with its inverse.
  13. ^ From the category theory point of view, the collection monads emerge from adjunctions between a free functor and a functor from the category of sets to the category of monoids.
  14. ^ >> is a variant of in Haskell for when only the monadic effect matters, throwing away the underlying output instead of passing it forward.

References

  1. ^ Lippert, Eric (21 February 2013). "Monads, part one". Fabulous adventures in coding. Archived from the original on 3 September 2018. Retrieved 24 October 2018. {{cite web}}: Unknown parameter |deadurl= ignored (|url-status= suggested) (help)
  2. ^ a b c d e O'Sullivan, Bryan; Goerzen, John; Stewart, Don (2009). "Monads". Real World Haskell. Sebastopol, California: O'Reilly Media. chapter 14. ISBN 978-0596514983. {{cite book}}: External link in |chapterurl= (help); Unknown parameter |chapterurl= ignored (|chapter-url= suggested) (help)
  3. ^ a b c Moggi, Eugenio (1991). "Notions of computation and monads" (PDF). Information and Computation. 93 (1): 55–92. CiteSeerX 10.1.1.158.5275.
  4. ^ a b c Wadler, Philip (June 1990). Comprehending Monads. ACM Conference on LISP and Functional Programming. Nice, France. CiteSeerX 10.1.1.33.5381.
  5. ^ a b c d e Wadler, Philip (January 1992). The essence of functional programming. 19th Annual ACM Symposium on Principles of Programming Languages. Albuquerque, New Mexico. CiteSeerX 10.1.1.38.9516.
  6. ^ a b "Control.Monad". Hackage. haskell.org. Retrieved 7 October 2018.
  7. ^ "Monad (sans metaphors)". HaskellWiki. 1 November 2009. Retrieved 24 October 2018.
  8. ^ "Monad". HaskellWiki. 19 November 2017. Retrieved 7 October 2018.
  9. ^ "What a Monad is not". 7 October 2018.
  10. ^ De Meuter, Wolfgang (1997). Monads as a theoretical foundation for AOP (PDF). International Workshop on Aspect Oriented Programming at ECOOP. Jyväskylä, Finland. CiteSeerX 10.1.1.25.8262.
  11. ^ Moggi, Eugenio (June 1989). Computational lambda-calculus and monads (PDF). Fourth Annual Symposium on Logic in computer science. Pacific Grove, California. CiteSeerX 10.1.1.26.2787.
  12. ^ "Monad laws". HaskellWiki. haskell.org. Retrieved 14 October 2018.
  13. ^ "Functors, Applicative Functors and Monoids". Learn You a Haskell for Great Good!. Retrieved 15 October 2018.
  14. ^ "For a Few Monads More". Learn You a Haskell for Great Good!. Retrieved 15 October 2018.
  15. ^ a b Piponi, Dan (7 August 2006). "You Could Have Invented Monads! (And Maybe You Already Have.)". A Neighborhood of Infinity. Archived from the original on 24 October 2018. Retrieved 16 October 2018. {{cite web}}: Unknown parameter |deadurl= ignored (|url-status= suggested) (help)
  16. ^ "Some Details on F# Computation Expressions". Retrieved 9 October 2018.
  17. ^ "Do notation considered harmful". HaskellWiki. Retrieved 12 October 2018.
  18. ^ Peyton Jones, Simon L.; Wadler, Philip (January 1993). Imperative functional programming (PDF). 20th Annual ACM Symposium on Principles of Programming Languages. Charleston, South Carolina. CiteSeerX 10.1.1.53.2504.

Haskell monad tutorials

Older tutorials

Other documentation

Scala monad tutorials