(** Monads
Functional programs are nice, but sometimes we want effects.
It turns out that many of these effects can be structured in a similar way,
using so-called monads (a concept from category theory/ algebra).
They are an abstract way of capturing many common programming concepts such as
* partiality,
* error handling,
* statefulness,
* non-determinism
* continuations
in a functional programming language. A monad consists of a type
constructor
M : Type → Type
and two operations:
• The ret operation takes a value x : A from a plain type and injects it into
the monad. The value ret x has type M A.
• The bind operation takes a monadic value m : M A and applies a function
f : A → M B to it. The resulting value bind m f has type M B.
The purpose of these operations is best explained by an example. For that,
we consider the option monad: *)
Require Import List EqNat.
Notation "[ x ; .. ; y ]" := (cons x .. (cons y nil) ..).
Module Option.
Definition ret { A } ( x : A ) : option A := Some x .
Definition bind { A B }( m : option A ) ( f : A -> option B ) : option B :=
match m with
| Some x => f x
| None => None
end .
(** The option monad is used to model partiality: the constructor Some denotes
that a computation has succeeded, and the constructor None denotes that a
computation has failed. As shown in the above definition, option_ret models a
successful computation, and option_bind propagates failures. *)
(** The ret and bind functions have to satisfy the following monad laws:
bind (ret x) f = f x
bind m ret = m
bind (bind m f ) g = bind m (λx.bind (f x) g) *)
Lemma bind_of_return : forall {A B : Type}, forall (a:A) (f:A -> option B),
(bind (ret a) f) = (f a).
reflexivity. Qed.
Lemma return_of_bind : forall {A : Type}, forall (aM: option A),
bind aM ret = aM.
intros A [a|]; reflexivity.
Qed.
Lemma bind_associativity : forall {A B C : Type}, forall (aM: option A) (f:A -> option B) (g:B -> option C),
(bind (bind aM f) g) = (bind aM (fun a => bind (f a) g)).
intros A B C [a|]; reflexivity.
Qed.
(** Recall the index function from the chapter “Poly” of Software Foundations
that returns the nth element of a list, and yields None in case the element does
not exist. We can use option_bind to use it multiple times, without having to
perform explicit pattern matches on the results of index. *)
Fixpoint index { A } ( n : nat ) ( l : list A ) : option A :=
match l with
| nil => None
| a :: l' =>
if beq_nat n O then Some a else index ( pred n ) l'
end.
Definition test ( l : list nat ) : option nat :=
bind ( index 1 l ) ( fun n1 =>
bind ( index 3 l ) ( fun n2 =>
bind ( index 5 l ) ( fun n3 =>
ret ( n1 + n2 + n3 ) ) ) ).
(** The function test retrieves the 1st, 3rd and 5th of the list l and sums these
elements. If one of the elements does not exist, the whole function test fails by
returning None. Let us try it out: *)
Eval compute in test [1 ;2 ; 3; 4 ;5 ;6 ; 7; 8 ;9 ;10]. (* Some 12 *)
Eval compute in test [1;2;3;4]. (* None *)
(** Exercise 1 Write 2 positive tests and 2 negative tests for multiple uses of
index using option_ret and option_bind and prove these using the reflexivity
tactic. *)
End Option.
(** An important part of a monad is that it satisfies the monad laws: ret is a
neutral element for bind and binding two functions consecutively is the same as
binding one function that is composed using a bind. These laws are expressed
by the following Coq definition that is parametrized by a type constructor M and
the operations ret and bind: *)
(** The ret and bind functions have to satisfy the following monad laws:
bind (ret x) f = f x
bind m ret = m
bind (bind m f ) g = bind m (λx.bind (f x) g) *)
Definition monad { M : Type -> Type }
( ret : forall { A } , A -> M A )
( bind : forall { A B } , M A -> ( A -> M B ) -> M B ) :=
( forall A B ( f : A -> M B ) x , bind ( ret x ) f = f x ) /\
( forall A ( m : M A ) , bind m ret = m ) /\
( forall A B C ( f : A -> M B ) ( g : B -> M C ) ( m : M A),
bind ( bind m f ) g = bind m ( fun x => bind ( f x) g)).
(** Exercise 2 Prove that option satisfies the monad laws: *)
Lemma option_monad : monad ( @Option.ret ) ( @Option.bind ) . Admitted.
(** Another example is the list monad which can be used to deal with functions
that yield multiple values. The ret function yields the singleton list, and bind
applies a function to each element of the list and flattens the result. *)
Definition list_ret { A } ( x : A ) : list A := [ x ].
Fixpoint list_bind { A B }
( l : list A ) ( f : A -> list B ) : list B :=
match l with
| nil => nil
| x :: l => f x ++ list_bind l f
end.
(** Exercise 3 Implement a function that computes all permutations of a list:
Fixpoint permutations { A } ( l : list A ) : list ( list A ) := . *)
(** For example permutations [1;2;3] may yield [[1;2;3]; [2;1;3]; [2;3;1];
[1;3;2]; [3;1;2]; [3;2;1]] (it does not matter in which order the permuta-
tions appear in the result). You should use list_ret and list_bind, and you
may define at most one helper function using a Fixpoint. *)
(** Exercise 4 Prove that list satisfies the monad laws: *)
Lemma list_monad : monad ( @list_ret ) ( @list_bind ) . Admitted.
(** Yet another example is the state monad, which attaches state information to
a type. Given a type A, the corresponding type is a function that takes a state,
and yields a resulting state and the return value of type A. The ret function
produces a value without changing the state, and the bind function propagates
changes to the state: *)
Definition state ( A : Type ) : Type := nat -> ( nat * A ) .
(** We have represented states using natural numbers, but one could of course
use any type, or make the definition polymorphic in the type of states. For the
purpose of this exercise, this will not be necessary. *)
(** Exercise 5 Implement the operations state_ret and state_bind. Prove that
your operations satisfy the monad laws:
Lemma state_monad : monad ( @state_ret ) ( @state_bind ) . *)
(** In order to prove the above lemma, you need the functional extensionality axiom,
which states that functions f and g are equal if ∀x . f x = g x. It can be obtained
by adding the following to the beginning of your file: *)
Require Import FunctionalExtensionality.
Check @functional_extensionality.
(** We use the state monad to implement a function that relabels binary trees. Binary trees are defined as: *)
Inductive tree :=
| leaf : tree
| node : tree -> nat -> tree -> tree.
(** The relabel function should preserve the tree structure, but change the labels
such that they are numbed consecutively from left to right. For example:
10 1
5 16 become 2 5
2 7 3 4
In order to implement this function, we use the state monad to keep track of
a counter. Each time we encounter a node, we increase the counter and relabel
it. First we define a helper function to increase the counter. *)
(** Exercise 6 Write a function that produces the value of the state and in-
creases the value of the state by one:
Definition state_inc : state nat := fill out *)
(** Exercise 7 Write a function that relabels trees as described above:
Fixpoint relabel ( t : tree ) : state tree :=
You should use the monadic operations state_ret, state_bind and state_inc. *)
(** Exercise 8 Write 2 tests of the relabel function and prove these using the
reflexivity tactic. *)
(** Exercise 9 The size of a binary tree is defined as: *)
Fixpoint size ( t : tree ) : nat :=
match t with
| leaf => 0
| node l x r => S ( size l + size r )
end.
(** Write a lemma that states that the relabel function preserves the size of a
binary tree and prove this lemma. *)