# 4. Monads¶

In this chapter, we will describe a powerful abstraction known as a *monad*. A monad is a type constructor `m : Type → Type`

that comes equipped with two special operations, `return`

and `bind`

. If `α`

is any type, think of `m α`

as being a “virtual `α`

,” or, as some people describe it, “an `α`

inside a box.”

For a given monad `m`

, the function `return`

has type `Π {α : Type}, α → m α`

. The idea is that for any element `a : α`

, `return a`

produces the virtual version of `a`

, or puts `a`

inside the box.

Once we are inside the box, we cannot get out; there is no general way of taking an element of `m α`

and obtaining an element of `α`

. But the `bind`

operation gives us a way of turning some operations on `α`

into operations inside the monad. Specifically, for a given monad `m`

, the function `bind`

has type `Π {α β : Type}, m α → (α → m β) → m β`

. Suppose we have a function `f`

that, given any element `a : α`

, produces a virtual element of `β`

; in more prosaic terms, `f`

has type `α → m β`

. Suppose also that we have a virtual element of `α`

, that is, `ma : m α`

. If we could extract from `ma`

a corresponding element `a`

of `α`

, we could apply `f`

to it to get a virtual element of `β`

. We cannot do that in general, but `bind`

gives us a way of simulating the compound operation: it applies `f`

directly “inside the box,” and gives us an element of `m β`

.

As an example of how `bind`

and `return`

can be used, given any function `f : α → β`

, we can get a function `map f : m α → m β`

by defining `map f ma`

to be `bind ma (λ a, return (f a)`

. Roughly, given `ma : m α`

, the `bind`

reaches into the box, finds an associated `a`

, and then puts `f a`

back into the box.

For another example, given any element `mma : m (m α)`

, the expression `monad.bind mma id`

has type `m α`

. This means that even though we cannot in general extract an element of `β`

from `m β`

, we *can* do it when `β`

itself is a virtual type, `m α`

. The expression `monad.bind mma id`

reaches into the `m (m α)`

box, catches hold of an element of `m α`

, and simply leaves it in the `m α`

box.

If you have never come across the notion of a monad before, these operations will seem quite mysterious. But instances of `return`

and `bind`

arise in many natural ways, and the goal of this chapter is to show you some examples. Roughly, they arise in situations where `m`

is a type construction with the property that functions in the ordinary realm of types can be transported, uniformly, into functions in the realm of `m`

-types. This should sound quite general, and so it is perhaps not that surprising that monads be instantiated in many different ways. The power of the abstraction is not only that it provides general functions and notation the can be used in all these various instantiations, but also that it provides a helpful way of thinking about what they all have in common.

Lean implements the following common notation. First, we have the infix notation

```
ma >>= f
```

for `bind ma f`

. Think of this as saying “take an element `a`

out of the box, and send it to `f`

.” Remember, we are allowed to do that as long as the return type of `f`

is of the form `m β`

. We also have the infix notation,

```
ma >> mb
```

for `bind ma (λ a, mb)`

. This takes an element `a`

out of the box, ignores it entirely, and then returns `mb`

. These two pieces of notation are most useful in situations where the act of taking an element of the box can be viewed as inducing a change of state. In situations like that, you can think of `ma >>= f`

as saying “do `ma`

, take the result, and then send it to `f`

.” You can then think of of `ma >> mb`

more simply as “do `ma`

, then do `mb`

.” In this way, monads provide a way of simulating features of imperative programming languages in a functional setting. But, we will see, they do a lot more than that.

Thinking of monads in terms of performing actions while computing results is quite powerful, and Lean provides notation to support that perspective. The expression

```
do a ← ma, t
```

is syntactic sugar for `ma >>= (λ a, t)`

. Here `t`

is typically an expression that depends on `a`

, and it should have type `m β`

for some `β`

. So you can read `do a ← ma, t`

as reaching into the box, extracting an `a`

, and then continuing the computation with `t`

. Similarly, `do s, t`

is syntactic sugar for `s >> t`

, supporting the reading “do `s`

, then do `t`

.” The notation supports iteration, so, for example,

```
do a ← s,
b ← t,
f a b,
return (g a b)
```

is syntactic sugar for

```
bind s (λ a, bind t (λ b, bind (f a b) (λ c, return (g a b)))).
```

It supports the reading “do `s`

and extract `a`

, do `t`

and extract `b`

, do `f a b`

, then return the value `g a b`

.”

Incidentally, as you may have guessed, a monad is implemented as a type class in Lean. In other words, `return`

really has type

```
Π {m : Type → Type} [monad m] {α : Type}, α → m α},
```

and `bind`

really has type

```
Π {m : Type → Type} [monad m] {α β : Type}, m α → (α → m β) → m β.
```

In general, the relevant monad can be inferred from the expressions in which `bind`

and `return`

appear, and the monad structure is then inferred by type class inference.

There is a constraint, namely that when we use monads all the types we apply the monad to have to live in the same type universe. When all the types in question appear as parameters to a definition, Lean’s elaborator will infer that constraint. When we declare variables below, we will satisfy that constraint by explicitly putting them in the same universe.

## 4.1. The option monad¶

The `option`

constructor provides what is perhaps the simplest example of a monad. Recall that an element of `option α`

is either of the form `some a`

for some element `a : α`

, or `none`

. So an element `a`

of `option α`

is a “virtual `α`

” in the sense of being either an element of `α`

or an empty promise.

The associated `return`

is just `some`

: given an element `a`

of `α`

, `some a`

returns a virtual `α`

. It is also clear that we cannot go in the opposite direction: given an element `ma : option α`

, there is no way, in general, of producing an element of `α`

. But we can simulate extraction of such an element as long as we are willing to stay in the virtual land of `options`

, by defining `bind`

as follows:

```
def bind {α β : Type} (oa : option α) (f : α → option β) :
option β :=
match oa with
| (some a) := f a
| none := none
end
```

If the element `oa`

is `some a`

, we can simply apply `f`

to `a`

, and otherwise we simply return `none`

. Notice how the `do`

notation allows us to chain these operations:

```
universe u
variables {α β γ δ : Type.{u}} (oa : option α)
variables (f : α → option β) (g : α → β → option γ)
(h : α → β → γ → option δ)
example : option β :=
do a ← oa,
b ← f a,
return b
example : option δ :=
do a ← oa,
b ← f a,
c ← g a b,
h a b c
```

Think of `f`

, `g`

, and `h`

as being partial functions on their respective domains, where a return value of `none`

indicates that the function is undefined for the given input. Intuitively, the second example above returns `h a (f a) (g a (f a))`

, assuming `oa`

is `some a`

and all the subterms of that expression are defined. The expression `h a (f a) (g a (f a))`

does not actually type check; for example, the second argument of `h`

should be of type `β`

rather than `option β`

. But monadic notation allows us to simulate the computation of a possibly undefined term, where the bind operation serves to percolate a value of `none`

to the output.

## 4.2. The list monad¶

Our next example of a monad is the `list`

monad. In the last section we thought of a function `f : α → option β`

as a function which, on input `α`

, possibly returns an element of `β`

. Now we will think of a function `f : α → list β`

as a function which, on input `α`

, returns a list of possible values for the output. This monad is sometimes also called the `nondeterministic`

monad, since we can think of `f`

as a computation which may nondeterministically return any of the elements in the list.

It is easy to insert a value `a : α`

into `list α`

; we define `return a`

to be just the singleton list `[a]`

. Now, given `la : list α`

and `f : α → list β`

, how should we define the bind operation `la >>= f`

? Intuitively, `la`

represents any of the possible values occurring in the list, and for each such element `a`

, `f`

may return any of the elements in `f a`

. We can then gather all the possible values of the virtual application by applying `f`

to each element of `la`

and merging the results into a single list:

```
def bind {α β : Type} (la : list α) (f : α → list β) : list β :=
join (map f la)
```

Since the example in the previous section used nothing more than generic monad operations, we can replay it in the `list`

setting:

```
universe u
variables {α β γ δ : Type.{u}} (la : list α)
variables (f : α → list β) (g : α → β → list γ)
(h : α → β → γ → list δ)
example : list δ :=
do a ← la,
b ← f a,
c ← g a b,
h a b c
```

Now think of the computation as representing the list of all possible values of the expression `h a (f a) (g a (f a))`

, where the bind percolates all possible values of the subexpressions to the final output.

Notice that the final output of the expression is a list, to which we can then apply any of the usual functions that deal with lists:

```
open list
variables {α β γ δ : Type} (la : list α)
variables (f : α → list β) (g : α → β → list γ) (h : α → β → γ → list δ)
example : ℕ :=
length
(do a ← la,
b ← f a,
c ← g a b,
h a b c)
```

We can also move `length`

inside the `do`

expression, but then the output lives in `ℕ`

instead of a `list`

. As a result, we need to use `return`

to put the result in a monad:

```
open list
variables {α β γ δ : Type} (la : list α)
variables (f : α → list β) (g : α → β → list γ)
(h : α → β → γ → list δ)
example : list ℕ :=
do a ← la,
b ← f a,
c ← g a b,
return (length (h a b c))
```

## 4.3. The state monad¶

Let us indulge in science fiction for a moment, and suppose we wanted to extend Lean’s programming language with three global registers, `x`

, `y`

, and `z`

, each of which stores a natural number. When evaluating an expression `g (f a)`

with `f : α → β`

and `g : β → γ`

, `f`

would start the computation with the registers initialized to `0`

, but could read and write values during the course of its computation. When `g`

began its computation on `f a`

, the registers would be set they way that `g`

left them, and `g`

could continue to read and write values. (To avoid questions as to how we would interpret the flow of control in terms like `h (k₁ a) (k₂ a)`

, let us suppose that we only care about composing unary functions.)

There is a straightforward way to implement this behavior in a functional programming language, namely, by making the state of the three registers an explicit argument. First, let us define a data structure to hold the three values, and define the initial settings:

```
structure registers : Type := (x : ℕ) (y : ℕ) (z : ℕ)
def init_reg : registers := registers.mk 0 0 0
```

Now, instead of defining `f : α → β`

that operates on the state of the registers implicitly, we would define a function `f₀ : α × registers → β × registers`

that operates on it explicitly. The function `f₀`

would take an input `a : α`

, paired with the state of the registers at the beginning of the computation. It could the do whatever it wanted to the state, and return an output `b : β`

paired with the new state. Similarly, we would replace `g`

by a function `g₀ : β × registers → γ × registers`

. The result of the composite computation would be given by `(g₀ (f₀ (a, init_reg))).1`

. In other words, we would pair the value `a`

with the initial setting of the registers, apply `f₀`

and then `g₀`

, and take the first component. If we wanted to lay our hands on the state of the registers at the end of the computation, we could do that by taking the second component.

The biggest problem with this approach is the annoying overhead. To write functions this way, we would have to pair and unpair arguments and construct the new state explicitly. A key virtue of the monad abstraction is that it manages boilerplate operations in situations just like these.

Indeed, the monadic solution is not far away. By currying the input, we could take the input of `f₀`

equally well to be `α → registers → β × registers`

. Now think of `f₀`

as being a function which takes an input in `α`

and returns an element of `registers → β × registers`

. Moreover, think of this output as representing a computation which starts with a certain state, and returns a value of `β`

and a new state. Lo and behold, *that* is the relevant monad.

To be precise: for any type `α`

, the monad `m α`

we are after is `registers → α × registers`

. We will call this the state monad for `registers`

. With this notation, the function `f₀`

described above has type `α → m β`

, the function `g₀`

has type `β → m γ`

, and the composition of the two on input `a`

is `f a >>= g`

. Notice that the result is an element of `m γ`

, which is to say, it is a computation which takes any state and returns a value of `γ`

paired with a new state. With `do`

notation, we would express this instead as `do b ← f a, g b`

. If we want to leave the monad and extract a value in `γ`

, we can apply this expression to the initial state `init_reg`

, and take the first element of the resulting pair.

The last thing to notice is that there is nothing special about `registers`

here. The same trick would work for any data structure that we choose to represent the state of a computation at a given point in time. We could describe, for example, registers, a stack, a heap, or any combination of these. For every type `S`

, Lean’s library defines the state monad `state S`

to be the monad that maps any type `α`

to the type `S → α × S`

. (In the Lean implementation, the data is stored in a single field of a structure.) The particular monad described above is then simply `state registers`

.

Let us consider the `return`

and `bind`

operations. Given any `a : α`

, `return a`

is given by `λ s, (a, s)`

. This represents the computation which takes any state `s`

, leaves it unchanged, and inserts `a`

as the return value. The value of `bind`

is tricker. Given an `sa : state S α`

and an `f : α → state S β`

, remember that `bind sa f`

is supposed to “reach into the box,” extract an element `a`

from `sa`

, and apply `f`

to it inside the monad. Now, the result of `bind sa f`

is supposed to be an element of `state S β`

, which is really a function `S → β × S`

. In other words, `bind sa f`

is supposed to encode a function which operates on any state to produce an element of `β`

to a new state. Doing so is straightforward: given any state `s`

, `sa s`

consists of a pair `(a, s₀)`

, and applying `f`

to `a`

and then `s₀`

yields the required element of `β × S`

. Thus the def of `bind sa f`

is as follows:

```
λ s, match (sa s) with (a, s₀) := b a s₀
```

The library also defines operations `get`

and `put`

as follows:

```
namespace hidden
def get {S : Type} : state S S :=
⟨λ s, (s, s)⟩
def put {S : Type} : S → state S unit := λ s₀, ⟨λ s, ((), s₀)⟩
end hidden
```

With the argument `S`

implicit, `get`

is simply the state computation that does not change the current state, but also returns it as a value. The value `put s₀`

is the state computation which replaces any state `s`

by `s₀`

and returns `unit`

. Notice that it is convenient to use `unit`

for the output type any operation that does not return a value, though it may change the state.

Returning to our example, we can implement the register state monad and more focused get and put operations as follows:

```
def init_reg : registers :=
registers.mk 0 0 0
@[reducible] def reg_state := state registers
def get_x : reg_state ℕ :=
do s ← get, return (registers.x s)
def get_y : reg_state ℕ :=
do s ← get, return (registers.y s)
def get_z : reg_state ℕ :=
do s ← get, return (registers.z s)
def put_x (n : ℕ) : reg_state unit :=
do s ← get,
put (registers.mk n (registers.y s) (registers.z s))
def put_y (n : ℕ) : reg_state unit :=
do s ← get,
put(registers.mk (registers.x s) n (registers.z s))
def put_z (n : ℕ) : reg_state unit :=
do s ← get,
put (registers.mk (registers.x s) (registers.y s) n)
```

We can then write a little register program as follows:

```
open nat
def foo : reg_state ℕ :=
do put_x 5,
put_y 7,
x ← get_x,
put_z (x + 3),
y ← get_y,
z ← get_z,
put_y (y + z),
y ← get_y,
return (y + 2)
```

To see the results of this program, we have to “run” it on the initial state:

```
#reduce foo.run init_reg
```

The result is the pair `(17, {x := 5, y := 15, z := 8})`

, consisting
of the return value, `y`

, paired with the values of the three
registers.

## 4.4. The IO monad¶

We can finally explain how Lean handles input and output: the constant `io`

is axiomatically declared to be a monad with certain supporting operations. It is a kind of state monad, but in contrast to the ones discussed in the last section, here the state is entirely opaque to Lean. You can think of the state as “the real world,” or, at least, the status of interaction with the user. Lean’s axiomatically declared constants include the following:

```
import system.io
open io
#check (@put_str : string → io unit)
#check (@get_line : io string)
```

The expression `put_str s`

changes the `io`

state by writing `s`

to output; the return type, `unit`

, indicates that no meaningful value is returned. The expression `get_line`

, in contrast, does not take any arguments. However you want to think of the change in `io`

state, a `string`

value is returned inside the monad. When we use the native virtual machine interpretation, thinking of the `io`

monad as representing a state is somewhat heuristic, since within the Lean language, there is nothing that we can say about it. But when we run a Lean program, the interpreter does the right thing whenever it encounters the bind and return operations for the monad, as well as the constants above. In particular, in the example below, it ensures that the argument to `put_str`

is evaluated before the output is sent to the user, and that the expressions are printed in the right order.

```
#eval put_str "hello " >> put_str "world!" >> put_str (to_string (27 * 39))
```