Monad
In functional programming, a monad is a type constructor together with two operations: , which wraps a plain value, and (bind), which runs a computation and passes its result to a function that produces the next one. Code with some extra behaviour, such as failing, producing several results, reading configuration, threading state or performing I/O, can then be written as a sequence of ordinary-looking steps, and the behaviour is defined once, in bind.
The notion comes from category theory, where it was studied from the late 1950s. Moggi used monads in 1989 to give a uniform semantics to computational effects[1][2], and Wadler showed how to use them to structure functional programs[3][4]. Haskell adopted monadic I/O in version 1.3[15]. Monads are a type class in Haskell and a module signature in OCaml, and the same structure appears in mainstream languages under names like flatMap (Scala, Java), and_then (Rust) and SelectMany (C#).
Overview
Consider three lookups in a row, each of which can fail. Written directly, every step is a match on the result of the previous one, and the failure case is repeated at every level:
Looking up an employee's manager's email, with and without bind.
(* Three lookups, each of which can fail. Without a monad every step is a match. *)let find k l = List.assoc_opt k llet manager_email_nested employees managers emails name =match find name employees with| None -> None| Some dept -> (match find dept managers with| None -> None| Some boss -> (match find boss emails with None -> None | Some e -> Some e))(* With bind, the None case is written once, inside bind. *)let ( let* ) m f = match m with Some x -> f x | None -> Nonelet manager_email employees managers emails name =let* dept = find name employees inlet* boss = find dept managers infind boss emails
Running it.
manager_email "ada" -> Some [email protected]manager_email "brom" -> Nonesame as nested match -> true
Every step has the same shape: run a computation, and if it produced a value, pass that value to the next step. let*, which is bind for option, captures exactly that, and the None case is written once, inside it. Other monads differ only in what it means for a computation to produce a value and for that value to be passed on: a list produces several, a state computation produces one along with a new state, an I/O action produces one after interacting with the world.
Definition
A monad consists of a type constructor and two operations
that satisfy the laws below. In Haskell they are the methods of the Monad class; in OCaml they are the contents of a module matching a signature, and a functor derives everything else from them.
The monad interface as an OCaml signature, and the operations every monad gets from return and bind.
(* The interface every monad provides. *)module type MONAD = sigtype 'a tval return : 'a -> 'a tval bind : 'a t -> ('a -> 'b t) -> 'b tend(* Everything else follows from return and bind, once, for every monad. *)module Extend (M : MONAD) = structinclude Mlet ( let* ) = bindlet map f m = bind m (fun x -> return (f x))let join mm = bind mm (fun m -> m)let ( >=> ) f g x = bind (f x) glet rec sequence = function| [] -> return []| m :: ms -> bind m (fun x -> bind (sequence ms) (fun xs -> return (x :: xs)))let traverse f xs = sequence (List.map f xs)endmodule Option_m = Extend (structtype 'a t = 'a optionlet return x = Some xlet bind m f = match m with Some x -> f x | None -> Noneend)module List_m = Extend (structtype 'a t = 'a listlet return x = [ x ]let bind m f = List.concat_map f mend)
Running it.
Option_m.traverse parse ["1"; "2"; "3"] -> Some [1; 2; 3]Option_m.traverse parse ["1"; "x"; "3"] -> NoneList_m.sequence [[1; 2]; [3; 4]] -> [[1; 3]; [1; 4]; [2; 3]; [2; 4]]List_m.join [[1]; [2; 3]; []] -> [1; 2; 3]
Laws
The identity laws say that adds no behaviour of its own. Associativity says that the way a chain of binds is nested does not matter, which is what makes a flat sequence of steps, as written with do-notation or let*, unambiguous. The laws are not checked by any compiler; they are what allows code using a monad to be refactored, such as extracting a few steps into a helper function, without changing its meaning.
The laws checked on random lists and random functions for the list monad.
(* The laws, checked on random inputs for the list monad. *)let return x = [ x ]let bind m f = List.concat_map f mlet () = Random.init 7let rand_list () = List.init (Random.int 4) (fun _ -> Random.int 10)(* Random functions int -> int list, built from a random table. *)let rand_fun () =let table = Array.init 10 (fun _ -> rand_list ()) infun x -> table.(abs x mod 10)let left_identity () = let a = Random.int 10 and f = rand_fun () in bind (return a) f = f alet right_identity () = let m = rand_list () in bind m return = mlet associativity () =let m = rand_list () and f = rand_fun () and g = rand_fun () inbind (bind m f) g = bind m (fun x -> bind (f x) g)
Running it.
left identity holds on 10000 random casesright identity holds on 10000 random casesassociativity holds on 10000 random cases
Kleisli composition
The laws are easier to remember in terms of Kleisli composition, which composes functions that return monadic values:
In that form the three laws say that is an identity for and that is associative:
so functions form a category, the Kleisli category of the monad.
Join and map
An equivalent definition replaces bind by join, which removes one layer of structure, together with fmap:
and . For lists join is concatenation; for option it turns Some (Some x) into Some x and anything else into None. This is the form used in category theory.
Notation
Chains of binds with a lambda at each step are hard to read, so most languages with monads provide syntax for them. Haskell's do-notation is defined by three rewriting rules:
OCaml has binding operators instead: once let* is defined as bind, let* x = e in body means bind e (fun x -> body). Other languages have their own forms:
Bind and its syntax in several languages.
Haskell >>= do-notationOCaml bind, let* let* x = e in ...F# Bind computation expressions: let! x = eScala flatMap for-comprehensions: for (x <- e) yield ...C# SelectMany LINQ query syntax: from x in e select ...Rust and_then the ? operator, for Option and ResultJava flatMap (method chains on Optional and Stream)
List comprehensions are do-notation for the list monad, and Wadler generalised them to comprehensions over any monad[3].
Examples
Option
The option monad (Maybe in Haskell) models computations that can fail without a reason. Bind stops at the first None; the Overview uses it.
Result
Result (Either in Haskell) is the same, but a failure carries an error value, and the first error ends the computation:
Validating two ages with Result.bind.
let ( let* ) = Result.bindlet parse_age s =match int_of_string_opt s with| None -> Error ("not a number: " ^ s)| Some n when n < 0 -> Error "negative age"| Some n -> Ok nlet older a b =let* x = parse_age a inlet* y = parse_age b inOk (max x y)
Running it. Only the first error is reported; reporting all of them needs an applicative instead.
older "36" "41" -> Ok 41older "36" "x" -> Error "not a number: x"older "-1" "x" -> Error "negative age"
Stopping at the first error is inherent: the second step is a function of the first step's result, and there is no result to apply it to. An applicative can report every error, because its steps do not depend on each other.
List
The list monad models nondeterminism: a computation produces any number of results, and bind runs the rest of the computation once for each, concatenating what they produce. A step that produces no results prunes that branch.
Pythagorean triples as a search.
let return x = [ x ]let ( let* ) xs f = List.concat_map f xslet guard b = if b then [ () ] else [](* Every choice of a, b, c; the ones that fail the guard contribute nothing. *)let triples n =let* a = List.init n succ inlet* b = List.init (n - a + 1) (fun i -> a + i) inlet* c = List.init (n - b + 1) (fun i -> b + i) inlet* () = guard ((a * a) + (b * b) = c * c) inreturn (a, b, c)
Running it.
triples 20 -> (3,4,5) (5,12,13) (6,8,10) (8,15,17) (9,12,15) (12,16,20)
Writer
A writer computation produces a value and some output, and bind concatenates the outputs. Here the output is a log:
Counting Collatz steps and logging each one.
module Writer = structtype 'a t = 'a * string listlet return x = (x, [])let bind (x, log) f = let y, log' = f x in (y, log @ log')let tell msg = ((), [ msg ])endlet ( let* ) = Writer.bindlet rec collatz n : int Writer.t =if n = 1 then Writer.return 0elselet next = if n mod 2 = 0 then n / 2 else (3 * n) + 1 inlet* () = Writer.tell (Printf.sprintf "%d -> %d" n next) inlet* steps = collatz next inWriter.return (steps + 1)
Running it.
collatz 6 -> 8 steps, log: 6 -> 3, 3 -> 10, 10 -> 5, 5 -> 16, 16 -> 8, 8 -> 4, 4 -> 2, 2 -> 1
The output only needs to be a monoid, so the same structure accumulates a sum, a maximum or a set.
Reader
A reader computation is a function of a shared, read-only environment, . Bind passes the same environment to both steps, which removes the need to thread a configuration value through every call by hand.
A greeting that depends on a configuration record.
module Reader = structtype ('e, 'a) t = 'e -> 'alet return x _ = xlet bind m f env = f (m env) envlet ask env = envendlet ( let* ) = Reader.bindtype config = { name : string; verbose : bool }let greeting : (config, string) Reader.t =let* c = Reader.ask inReader.return (if c.verbose then "Hello, " ^ c.name ^ ", welcome back." else "Hi " ^ c.name)
Running it.
greeting { verbose = true } -> Hello, Ada, welcome back.greeting { verbose = false } -> Hi Ada
State
A state computation is a function from a state to a result and a new state, . Bind runs the first computation on the current state and the second on the state it leaves behind:
A stack machine evaluating 2 3 + 4 * with the stack as state.
(* State: a computation that threads a value through, s -> a * s. *)module State = structtype ('s, 'a) t = 's -> 'a * 'slet return x s = (x, s)let bind m f s = let x, s' = m s in f x s'let get s = (s, s)let put s _ = ((), s)let run m s = m sendlet ( let* ) = State.bind(* A stack machine: the stack is the state. *)let push x = let* st = State.get in State.put (x :: st)let pop = let* st = State.get in match st with x :: rest -> let* () = State.put rest in State.return x | [] -> failwith "empty"let binop f = let* b = pop in let* a = pop in push (f a b)(* 2 3 + 4 * in reverse Polish notation *)let program = let* () = push 2 in let* () = push 3 in let* () = binop ( + ) in let* () = push 4 in let* () = binop ( * ) in pop
Running it.
run program [] -> result 20, final stack []
Continuation
A continuation computation receives the rest of the program as a function and decides how to call it:
This is continuation-passing style packaged as a monad. Because the continuation is a value, it can be ignored, called twice or stored, which gives early exit, backtracking and coroutines. callcc passes the current continuation to its argument:
Early exit from a fold with call/cc.
module Cont = structtype ('r, 'a) t = ('a -> 'r) -> 'rlet return x k = k xlet bind m f k = m (fun x -> f x k)(* call/cc hands the computation its own continuation, as a functionthat abandons whatever continuation it is later called under. *)let callcc f k = f (fun x _ -> k x) klet run m = m Fun.idendlet ( let* ) = Cont.bind(* Product of a list, leaving the whole computation as soon as a zero appears. *)let product xs =Cont.run(Cont.callcc (fun exit ->let rec go = function| [] -> Cont.return 1| 0 :: _ -> exit 0| x :: rest -> let* p = go rest in Cont.return (x * p)ingo xs))
Running it.
product [2; 3; 4] -> 24product [2; 0; 4] -> 0
I/O
Haskell has no side effects in expressions, so input and output are values of type IO a: descriptions of interactions that produce an a. The runtime system performs the one called main. Bind sequences two descriptions into one, and do-notation makes the result read like imperative code:
Haskell. The do block is getLine >>= \name -> putStrLn ("Hello, " ++ name).
main :: IO ()main = doname <- getLineputStrLn ("Hello, " ++ name)
Conceptually IO a is a state monad over the state of the world, , where the world can only be passed along, never copied; Peyton Jones and Wadler describe this design[5]. OCaml performs effects directly and does not need an I/O monad, but its promise libraries, such as Lwt, are monads over the same idea: a value that will be available later.
Parsers
A parser is a function from input to a result and the remaining input, or failure, which is the state monad combined with option or list. Bind runs one parser after another on what is left, and parser combinators are built from it.
Derived operations
Many functions are written once for all monads, in terms of return and bind:
sequence runs a list of computations in order and collects their results; for option it fails if any fails, and for lists it takes the cartesian product, as the output in the Definition section shows. traverse maps and then sequences, and is the usual way to apply a fallible function to every element of a list. The OCaml functor Extend above defines them once for every monad passed to it.
Relation to functors and applicative functors
Every monad is a functor, with fmap defined from bind as above, and an applicative functor, with
The difference between the three is what a later step may depend on. With fmap there is only one computation. With the applicative there are several, but each is fixed before any runs. With bind the next computation is chosen by a function of the previous result, which is strictly more powerful: it can decide whether to continue, and which computation to run next. The cost is that a monadic computation cannot be inspected before it runs[13]. Haskell's standard library has made Applicative a superclass of Monad since 2015.
Category theory
A monad on a category is a triple of an endofunctor and natural transformations and satisfying
With composition of endofunctors as the tensor product, these are exactly the associativity and unit laws of a monoid, which is the meaning of the phrase "a monad is a monoid in the category of endofunctors"[10]. The programming definition is the equivalent Kleisli triple form, with and [16].
Kleisli category
The Kleisli category has the objects of , arrows as its morphisms from to , identities , and composition
which is Kleisli composition, written the other way round.
Adjunctions
Every adjunction with gives a monad on [7], and every monad arises this way: the Kleisli category and the category of Eilenberg–Moore algebras give the smallest and largest adjunctions that produce it[8][9]. Two programming monads come from familiar adjunctions. The state monad comes from the currying adjunction :
and the list monad from the free monoid adjunction between sets and monoids, with , the finite sequences over , and concatenation.
Variations
Additive monads
Some monads also have a failing computation and a way to combine alternatives, with laws that make them a monoid compatible with bind:
For lists they are the empty list and concatenation; for option, None and taking the first Some. guard, as used in the list example, is defined from them.
Monad transformers
Monads do not compose in general: the composite of two monads is not always a monad. A monad transformer adds one effect on top of any monad, so effects can be stacked[11].
StateT as an OCaml functor, applied to option: state that can also fail.
(* A monad transformer adds one effect to any monad. StateT over option:state that can also fail, s -> (a * s) option. *)module StateT (M : sigtype 'a tval return : 'a -> 'a tval bind : 'a t -> ('a -> 'b t) -> 'b tend) = structtype ('s, 'a) t = 's -> ('a * 's) M.tlet return x s = M.return (x, s)let bind m f s = M.bind (m s) (fun (x, s') -> f x s')let lift (m : 'a M.t) : ('s, 'a) t = fun s -> M.bind m (fun x -> M.return (x, s))let get s = M.return (s, s)let put s _ = M.return ((), s)endmodule S = StateT (structtype 'a t = 'a optionlet return x = Some xlet bind m f = match m with Some x -> f x | None -> Noneend)let ( let* ) = S.bind(* Take the next token from the input; fail when it runs out. *)let next = let* input = S.get in match input with t :: rest -> let* () = S.put rest in S.return t | [] -> S.lift Nonelet number = let* t = next in S.lift (int_of_string_opt t)let sum_of_two = let* a = number in let* b = number in S.return (a + b)
Running it.
sum_of_two ["2"; "40"; "x"] -> Some (42, [x])sum_of_two ["2"; "x"] -> Nonesum_of_two ["2"] -> None
The order of a stack matters. is , so a failure discards the state. is , so the state reached before the failure survives. Effect handlers are the other widely used answer to combining effects.
Free monads
For a functor , the free monad is the tree of -shaped layers ending in values:
It is a monad for any functor, and a program written in it is a data structure describing its effects without performing them, which separate interpreters can then run, test or translate[12].
Comonads
A comonad is the categorical dual: and . Where a monad builds context around a value, a comonad computes from a value in context: streams, zippers focused on one position, and cellular automata, where each cell's next state depends on its neighbours[14].
History
Godement described the structure in 1958 as a "standard construction"[6]. Huber showed in 1961 that every adjunction gives one[7], and in 1965 Kleisli and, independently, Eilenberg and Moore showed that every one arises from an adjunction[8][9]. It was called a triple in that period; the name monad came later and became standard through Mac Lane's textbook[10].
Moggi proposed monads in 1989 as a way to structure the semantics of programming languages, with one monad for each notion of computation: partiality, exceptions, state, nondeterminism, continuations[1][2]. Wadler turned the idea into a programming technique for pure functional languages[3][4], Peyton Jones and Wadler used it for I/O in Haskell[5], and Haskell 1.3 made monadic I/O part of the language[15]. Monad transformers followed in 1995[11].
Outside functional languages the structure is common without the name. JavaScript promises come close: then acts as bind, but it flattens nested promises automatically, so a promise of a promise cannot exist and the laws hold only up to that. Java's Optional and Stream, Rust's Option and Result with the ? operator, and C#'s LINQ are all built around a bind.
see also
- FunctorA type constructor f with a map operation, fmap : (a -> b) -> f a -> f b, that preserves identity and composition. Lists, options, trees and functions out of a fixed type are functors. In OCaml the word also means something else: a module parametrised by another module, such as Map.Make.
- ApplicativeA functor with pure : a -> f a and a way to combine independent computations, <*> : f (a -> b) -> f a -> f b, or equivalently product : f a -> f b -> f (a * b). Every monad is applicative, but applicatives are strictly more general: because later steps cannot depend on earlier results, effects can be accumulated, parallelised or inspected before running.
- CPSContinuation-passing style: instead of returning, a function takes an extra continuation argument and calls it with the result. Every call becomes a tail call, which is what lets a CPS-transformed program run in constant stack space wherever tail calls are eliminated, and it makes control flow a value that can be stored and resumed.
- Effect handlerA construct that runs code which may perform an effect, and handles each effect by receiving it together with the continuation from the point where it was performed. OCaml 5 has them, with one-shot continuations: each can be resumed at most once.
- Parser combinatorA parser is a function from input to a result and the rest of the input, or failure. Parser combinators build parsers from smaller ones: sequencing, choice, repetition. A grammar becomes a set of ordinary recursive definitions in the host language.
- Yoneda lemmaFor a functor F and an object a, natural transformations from Hom(a, -) to F correspond one to one with elements of F a. In Haskell, forall b. (a -> b) -> f b is isomorphic to f a, and the left-hand form turns a chain of fmaps into one composed function and a single fmap.
- ZipperA data structure with a focus: the subterm at the focus, plus the path back to the root together with everything not on it. Moving the focus and editing at it take constant time, and the type of contexts is the derivative of the structure's type.
further reading
- [1]E. Moggi, “Computational lambda-calculus and monads”, LICS (1989).
- [2]E. Moggi, “Notions of computation and monads”, Information and Computation 93 (1991).
- [3]P. Wadler, “Comprehending monads”, LISP and Functional Programming (1990).
- [4]P. Wadler, “Monads for functional programming”, Advanced Functional Programming, LNCS 925 (1995).
- [5]S. L. Peyton Jones, P. Wadler, “Imperative functional programming”, POPL (1993).
- [6]R. Godement, Topologie algébrique et théorie des faisceaux, Hermann (1958).
- [7]P. J. Huber, “Homotopy theory in general categories”, Mathematische Annalen 144 (1961).
- [8]H. Kleisli, “Every standard construction is induced by a pair of adjoint functors”, Proceedings of the AMS 16 (1965).
- [9]S. Eilenberg, J. C. Moore, “Adjoint functors and triples”, Illinois Journal of Mathematics 9 (1965).
- [10]S. Mac Lane, Categories for the Working Mathematician, Springer (2nd ed., 1998).
- [11]S. Liang, P. Hudak, M. Jones, “Monad transformers and modular interpreters”, POPL (1995).
- [12]W. Swierstra, “Data types à la carte”, Journal of Functional Programming 18 (2008).
- [13]C. McBride, R. Paterson, “Applicative programming with effects”, Journal of Functional Programming 18 (2008).
- [14]T. Uustalu, V. Vene, “Comonadic notions of computation”, Electronic Notes in Theoretical Computer Science 203 (2008).
- [15]J. Peterson, K. Hammond (eds.), Report on the Programming Language Haskell, Version 1.3 (1996).
- [16]E. G. Manes, Algebraic Theories, Graduate Texts in Mathematics 26, Springer (1976).