"The monad is nothing but a simple substance which
enters into composite entities; simple, that is to say,
without parts" (Leibniz. Monadology).
Monad in category theory
The concept of monad belongs to category theory.
A category consists of a class (a set of objects) and a set of morphisms. A morphism (or arrow) is a directed relation between two objects A and B of the class (a binary relation), which is expressed as A→B.
A category C satisfies the following two axioms:
Associative composition of morphisms:
h ο (g ο f) = (h ο g) ο f
Morphism identity. For every object x of C there exists a morphism identity 1x which makes it correspond to itself.
A functor (or functor) is a function from one category to another that carries objects to objects and morphisms to morphisms so that the composition of morphisms and identities are preserved. An endofunctor is a functor of a category on itself.
A natural transformation transforms one functor into another while respecting its internal structure, i.e. the composition of morphisms, of the categories involved. It can be considered as a morphism of functors.
Having reviewed these concepts of category theory, we can give the formal definition of monad over a category C, also called "triple" because it is defined by three axioms:
A T. T: C → C
Two natural transformations:
η: 1T → T (natural transformation of the morphism identity of T in T)
μ: T2 → T (T2 indicates applying T twice).
We can consider a monad as a category in which objects are states of a system and morphisms are transition functions between states.
Monads in functional programming languages
Since the key concept of category theory is that of morphism, monads are being applied in functional programming languages. These languages are of two types:
The "pure" ones, which are those that exclusively use functions, with referential transparency (the property of always obtaining the same results with the same input values and without collateral effects). The advantages of pure functional languages are: they are easier to understand (the data flow is explicit) and can have lazy evaluation (lazy). The disadvantages are that the functional paradigm is often insufficient, since it needs other paradigms, mainly the imperative paradigm. Pure functional languages are Haskell and Miranda.
The "impure" ones, which are those that include, in addition to functions, resources of an imperative language. The advantages of the impure ones are: greater efficiency and more compact expressions. Functional impure languages are Scheme and Standard ML.
The trend is to integrate both aspects in functional languages. One of the mechanisms used for this purpose is precisely the use of monads. Monads are used as a complementary mechanism for pure functional languages to support the imperative paradigm. In functional programming, a monad is a structure that represents a sequence of transformations of an object. To use monads in a functional language, it is not necessary to have prior knowledge of category theory.
The basic idea of a monad in functional programming is as follows:
A function f is replaced by a more general m (the monad), so that within it the original (f) is invoked and an additional process is performed. The result of the monad function (m) is the same as if f had been invoked directly. Thus, a monad is a higher-order function that includes the original function and performs an additional imperative-type process.
The monad function is also called a "container", since it contains the original function.
Monads in Haskell
Haskell [Bird, 2000], one of the best known pure functional languages, uses monads. In Haskell the three monad functions are reduced to two: return and bind (bind, bind). The latter is represented as ">>=".
In Haskell, by definition, M is a monad when it possesses the following two operations:
return :: a → M a
This operation admits several interpretations:
Is a container or monad M containing the basic value or type a.
The basic value or type a is of type M.
The monadic type M a represents a computation that produces the basic value or type a.
For example, in the container interpretation, if I have an apple (a), I can put it in a box (M).
(>>=) :: M a → (a → M b) → M b
Combines two computations in sequence, passing the result of the first as an argument of the second. This operation is associative.
The function (a → M b) is applied with the input value (M a), whose result is M(M b)) which is put together to obtain a container of type (M b).
For example, if I have a box of apples (M a), and if for each apple I get a box of pears, then I can get a box with all pears together.
Haskell also defines the meta-function fmap, which applies a function f to all the elements of a container, such that the objects a, b, c, ... of the container are transformed into f(a), f(b), f(c), ... and morphisms of type a → b are transformed into f(a) → f(b). It is defined as follows:
fmap :: (a → b) → (fa → fb)
For example, if from each apple I get a pear (a → b), from a box of apples I get a box of pears (fa → fb).
The function fmap is a particular case of the so-called "lifting monadic", where basic values are replaced by monadic values, i.e., it generalizes a function by replacing arguments by containers. In general,
f(T1 … Tn) → r ⇒ f(MT1 ... MTn) → Mr
Monads transformed Haskell into an imperative programming language. Haskell popularized the use of monads for imperative programming with the notation do.
Monads vs. Continuations
A continuation can be expressed as follows: f(x, c) = c(f(x)), that is, the continuation c is a function that is a parameter of another function. For example,
There is a close relationship between monads and continuations. In both cases an additional function must be specified:
In the case of monads, this additional function is specified outside the program execution flow environment.
In the case of continuations, it is specified inside the main program.
If monads are used, the specification of continuations becomes simpler, clearer and easier to maintain, since the whole program does not have to be modified, only the monads have to be redefined.
Eugenio Moggi [1989] has shown that the concept of monad is more general than that of continuation. In other words, a continuation is a particular case of a monad. The composition of continuations has monadic structure.
Monads, abstract algebra and universal algebra
A monad is essentially a monoid in an endophytic category. In abstract algebra, a monoid is an algebraic structure with an internal binary operation, which is associative and has a neutral element. That is, a monad is a semigroup with a neutral element. A semigroup is a set with an internal associative operation.
Universal algebra is a generalization of abstract algebra that studies the common and generic properties of all algebraic operations and structures on a set. It is a kind of algebraic meta-theory based on a small number of axioms.
Category theory and universal algebra aim at the grounding of all algebraic structures. Universal algebra is based on the internal operations that define the structure. Category theory is based on morphisms.
Universal algebra can be formulated in terms of category theory, in particular it can be defined in terms of monads. Therefore, with monads any algebraic structure can be constructed. There is an equivalence between universal algebra and monads in the category Sets (the category of sets). Monads allow to express in a compact form certain aspects of the universal algebra.
Monads provide a different view of universal algebra. For example, varieties can be represented by monads. Universal algebra is essentially the study of varieties. A manifold is a generalization of curve (1-manifold) and surface (2-manifold) to n dimensions.
Advantages and limitations of monads in pure functional languages
The advantages are:
They supplement the shortcomings of pure functional languages. Monadic programming allows writing functional programs that resemble imperative programs. Many imperative programming concepts are defined in a purely functional way, without the need to extend the semantics of the language.
They clearly separate the imperative and functional components of a program.
They provide modularity. Each monad is a module.
They allow to create languages embedded in the functional language or to create domain-specific languages.
They implement interaction actions with the environment and allow to address a number of specific problems with little modification of the program that implements the functionality.
They provide flexibility. Functional programs with monads are more adaptable, because changes are easier, since they are basically made in one place (in the monad definition), rather than throughout the source program. Functions are composed differently depending on the monad used.
The readability of the program is improved and programming is simplified, freeing the programmer from including the same code at every point in the program where it is needed.
With monads the programmer does not need to make explicit the types of parameters required by each function. The bind function is in charge of parameter passing. Using monads, the source code looks more like a string of function names, with no explicit calling mechanisms from one function to another. Monads blur the distinction between objects and morphisms.
Monads favor the use of combinators in functional programming. A combinator is a function that constructs a piece of source code from other pieces of source code. Functional programming makes extensive use of combinators to build programs. With combinators, programming becomes simpler and more powerful because they are high-level mechanisms that allow you to dispense with (low-level) details.
Examples of generic combinators are fmap (apply a function to the elements of a list) and filter (select the elements of a list that meet a condition). An example of a specific combinator is parsing.
And the constraints are important:
They are unintuitive, difficult to understand and use, due to their abstract nature.
It is not a sufficiently generic mechanism. They are only applied in functional languages and by means of functions.
They break the elegance of pure functional programs. There are two programming styles: the functional programming style and the monadic programming style. Although in execution there is obviously a connection between the two, in coding they are separate.
They have combinatorial limitations and it is complex to create higher order monads. There is no general technique for combining monads. Special monads called "monad transformers" are used to facilitate monad combinatorics.
A monad transformer [King and Wadler, 1992] is a monad parameterized over another monad, such that computations on the second monad can be raised to computations on the first (the new monad). By combining several monad transformers, a large manifold of higher-order monads can be constructed.
Implementing Monads in MENTAL
Category theory vs. MENTAL
Category theory is a theory with a simple foundation (binary relations between objects). But paradoxically it is a complex theory, unnatural, too abstract and not very intuitive for two reasons: 1) because it is based on a single conceptual primitive (the morphism); 2) because this primitive is ambiguous, it has no defined semantics, it is open, since the concept of morphism admits many interpretations.
Moreover, category theory is unsuitable for grounding mathematics and unsuitable for application to programming. The true categories are the universal semantic primitives, which establish the degrees of freedom. [see Applications - Mathematics - Category Theory].
Monads vs. generic expressions
The concept of monad is so confusing that it has been attempted to be defined in many ways, including the following:
An overload (overload) of operations, i.e., the assignment of new semantics to operations.
A standardized pattern or interface for combining functions.
A way to compose functions but with conditions.
A container.
An endofunctor acting on the objects and morphisms of a container.
A computational pattern.
A design pattern that allows for modeling side effects in functional programming.
A higher order function or a way of generalizing functions.
A kind of conveyor belt or assembly line. An object undergoes a series of transformations, where the result of each step is the input to the next.
It has even been claimed that the monad concept is ineffable, that it is not possible to define it and that we can only use it in concrete applications.
The definition that seems perhaps closest to the concept is the following: a monad is a design pattern for composing functions with extended types. An extended type is a more general type that includes the basic type.
However, this definition is not clear enough. Monads are really generic expressions in disguise. With MENTAL generic expressions the mechanisms of monads are simplified and clarified. Moreover, their limitations are overcome.
The monads of functional languages are placed at a "meta" level and globally affect the program in some computational aspects of a pure functional program. In this sense there is an analogy with aspect-oriented or event-driven programming.
Generic expressions (parameterized or not) allow to express what monads do. They can use all the resources of the language, in a truly generic way and in a simpler and more modular way. Moreover, there is no need to modify the source program.
In MENTAL generic expressions are not linked to functions, but to all expressions of the language. Monad functionalities can be expressed more simply and directly with MENTAL.
Monads cannot be combined in a general way. To combine one monad with another, one must define a monad transformer that converts one monad into another. In MENTAL, primitives are freely combined.
With MENTAL you can apply any programming paradigm, not only the functional paradigm. The future of programming is not about monads but about the supreme level of abstraction. Moreover, MENTAL is a universal paradigm, and not only in programming. The level of abstraction of MENTAL is supreme, so it allows to express the theory of categories, monads, abstract algebra and universal algebra. It underlies all formal sciences.
The operation bind of monads can be expressed thus in MENTAL:
〈( M=a → a=(M=b) → M=b )〉
If a computation M produces a (or an expression M evaluates to a) and if < code>a produces a computation that produces b, then the computation M produces b.
Examples
On the subject of the relationship between objects and transformations, there are two extreme processes:
The same function is applied successively to several objects in a sequence. It corresponds to the fmap meta-function of some functional programming languages such as Haskell and Lisp. It is a higher-order function that applies the initial function to each element of a list, returning a list of results in the same order.
The same object is subjected to several successive transformations.
In practice, these two types of processes can become confused, since a sequence of objects can be considered a single object.
These two processes can be specified in an extremely simple way in MENTAL.
In the first case, the meta-function fmap is expressed as follows:
〈( (fmap f x) = ( ( [f([x↓])] ) )〉
(f applies to each of the components of x)
Examples:
〈( f(x) = x*x )〉)
(a = (1 2 3 4))
(fmap f a) // ev. (1 4 9 16)
〈( f(x) = (xx+10)↓ )〉
(a = (1 2 3 4))
(fmap f a) // ev. (1 11 2 12 3 13 4 14)
We want to subject a numerical sequence to a transformation such that each element of the sequence is transformed into two sequences:
f: n → (n, n), (n+1, n+2).
For example, if we have the object (1, 2, 3, 4), the result is.
In this example, the functions can be chained together without problems. But there are times when chaining cannot be done mechanically when there are errors, for example, when trying to calculate the square root of a negative number. For example:
We define 〈( fοg = g(f) )〉 (composition of functions)
with the associative property:
〈( (fοg)οh ≡ fο(gοh) )〉.
For example,
(sqrt(r) ο sum5(r))
evaluates to (sum5(sqrt(r))),
being 〈( sum5(r) = r+5 )〉.
We assume that the function sqrt(r) returns the square root of r if r>0 or the text "error" if r<0. The following function must first check, before applying it, that the result of the previous function is a number and not an error message. If it detects the error message, its response must also be the same error message. The new function would be:
(sum5(r) ← r≥0 →' "error")
This same mechanism should be applied to the following possible functions of the chain. In order not to repeat it, we can create a generic expression:
〈( (M(f(r)) = (f(r) ← r≥0 →' "error") )〉
Therefore, the concatenation expression of the two functions would be:
(sqrt(r) ο M(sum5(r)))
In this example, two output values could be specified (the result of the function and a message indicating whether or not an error occurred.
Addenda
Origin of monads
The term "monad" is borrowed from Leibniz, just as the term "category" is borrowed from Kant. For Leibniz, a monad is a simple substance that is part of all elements of reality.
Saunders Mac Lane, co-founder of category theory, together with Samuel Eilenberg adopted the philosophical term "monad" to refer to something general, an entity capable of generating all other entities. But that does not mean that the monad is simple, for the concept of monad in category theory is very abstract, complex and unintuitive, and therefore goes against Leibniz's conception. The popularity of the term ¨monad" is probably due to Mac Lane himself for his influential book Category Theory for the Working Mathematician. It is also believed that the term "monad" was motivated and stimulated by its similarity to the word "monoid", for a monad is a monoid.
Monads have been known by other names: triple or triad (for their definition by three axioms) and standard construction (for their claim to be something that grounds everything else).
In abstract algebra, a monoid is an algebraic structure with a single associative binary operation and an identity element.
The first monad to be constructed was presented by Roger Godemont in 1958 [Barr & Wells, 1985].
Eugenio Moggi [1989, 1990, 2000] was the first to introduce monads in computing, specifically in the area of denotational semantics [Stoy, 1997] to try to model their meaning. Denotational semantics is based on assigning to each language element a mathematical object.
Spivey [1990] found that monads provided an easy way to deal with exceptions.
Philip Wadler [1990, 1992, 1992, 1995, 1997] popularized Moggi's ideas by proposing monads as a general technique for extending functional languages to imperative programming.
Monads have been used in the development of the Glasgow Haskell compiler (which is written in Haskell).
Applications of monads
Monads are being applied, in various fields:
Exception and error handling.
Global states.
Input/output.
Array management (access, in-place update, etc.).
Program execution tracing.
Management of internal and external states of a program.
Data structure management.
Management of the order of operations (execution traces).
Memory management.
Communication between concurrent processes.
Parsing.
Interpreters and compilers.
Logging.
Interface to code written in other languages.
Continuations.
Management of a graphical user interface (GUI).
Metaprogramming.
Non-deterministic programming.
Predefined monads in Haskell
Haskell supplies predefined monads for use as functions and for combining functions.
Monad
Description
Maybe
Computations that may or may not return a result
Identity
Mónada identidad
[] (List)
Computations returning multiple results
IO
Computations that perform Input/Output
Error
Computations that may fail or throw exceptions
State
Computations that maintain state
Reader
Computers reading data
Writer
Computers that write data
Cont
Computers that can interrupt or restart themselves
Bibliography
Barr, M.; Walls, C. Toposes, Triples and Theories. A series of Comprehensive Studies in Mathematics. Springer, 1985.
Bird, Richard. Introducción a la Programación Funcional con Haskell. Prentice Hall, 2000.
Internet. Monads in Haskell. http://www.haskell.org.
King, David; Wadler, Phil. Combining Monads. In Glasgow Workshop on Functional Programming. Ayr, July 1992. Springer-Verlag.
Mac Lane, Saunders. Category Theory for the Working Mathematician. Graduate Texts in Mathematics. Springer, 1978.
Moggi, Eugenio. Computational lambda-calculus and monads. In IEEE Symposium on Logic in Computer Science. Asilomar, Calif. USA, 1989. Disponible online.
Moggi, Eugenio. An abstract view of programming languages. Technical Report ECS-LFCS-90-113, Laboratory for Foundations of Computer Science, University of Edinbourgh, Scotland, 1990.
Moggi, Eugenio. Notions of computation and monads. Information and Control, 93, 55-92, 2000.
Newbern, Jeff. All about Monads. http://www.nomaware.com / monads.html. [Buenos ejemplo de mónadas, con explicaciones].
Spivey, M. A functional theory of exceptions. Science of Computer Programming 14: 25-42, 1990.
Stoy, Joseph. Denotational Semantics. The Scott-Stratchey Approach to Programming Laguages Theory. MIT Press, 1997.
Wadler, Philip. Comprehending monads. In Conference on Lisp and Functional Programming, Nice, France. ACM, June 1990.
Wadler, Philip. The essence of functional programming. In Conference Record of the Nineteenth Annual ACM Symposium on Principles of Programming Languages, Alburquerque, New Mexico, pp. 1-14, January 1992.
Wadler, Philip. Monads for functional programming. Internet. Copia de artículo publicado en 1995. [Una buena intrducción].
Wadler, Philip. How to Declare Imperative. ACM Computing Surveys, 29(3): 240-263, 1997.