(** Monads
This is in preparation to the QuickChick chapter in book 4.
There's also a short bit about Monads in:
https://softwarefoundations.cis.upenn.edu/qc-current/Typeclasses.html
Functional programs are nice, but sometimes we want effects, for example I/O.
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.*)
(** 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]: *)
(** In equations:
bind (ret x) f = f x
bind m ret = m
bind (bind m f ) g = bind m (λx.bind (f x) g) *)
Generalizable Variable A B.
Class Monad {M:Type -> Type}
(ret: forall {A}, A -> M A)
(bind: forall {A B}, M A -> ( A -> M B ) -> M B):=
{bind_of_return : forall `(a:A) `(f:A -> M B),
(bind (ret a) f) = (f a);
return_of_bind : forall `(aM: M A), @bind _ A aM ret = aM;
bind_assoc : forall {A B C : Type},
forall `(aM: M A) `(f:A -> M B) (g:B -> M C),
(bind (bind aM f) g) = (bind aM (fun a => bind (f a) g))}.
(* 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. Print 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, ret models a
successful computation, and bind propagates failures.
We have seen the Option type constructor before.
*)
(** 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) *)
Local Instance OptionMonad: Monad (@Option.ret) (@Option.bind). Admitted.
(** 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 ret and bind and prove these using the reflexivity
tactic. *)
End Option.
(** 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.
Local Instance list_monad : Monad ( @list_ret ) ( @list_bind ) . Admitted.
(** Exercise 3 Implement a function that computes all permutations of a list:
Fixpoint permutations { A } ( l : list A ) : list ( list A ) := . *)
(** State *)
(** 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 S {A} : Type := S -> ( S * A ).
(** The idea is that a stateful function A * nat -> nat * B is the
same as a monadic function A -> (nat -> (nat * B)) *)
(** 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:
Local Instance 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.
(* Admitted. *)
(** We use the state monad to implement a function that relabels binary trees. Binary trees are defined as:
Aside: like lists, trees define a monad. *)
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.
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.
(** Other monads *)
(* We can use the do-notation.
Reminiscent of imperative programming. *)
(** Error *)
Require Import String.
Module Error.
Notation E:=string.
Definition M X:=(X+E)%type.
Definition ret {X}:=@inl X E.
(* If an error was raised stop the computation,
and just report the error. *)
Definition bind {A B:Type} ( m : M A ) ( f : A -> M B ) :=
match m with
inl x => f x
| inr e => inr e
end.
Local Instance error_monad: Monad (@ret) (@bind). Admitted.
Notation "c >>= f" := (bind c f) (at level 50, left associativity).
Notation "x <- c1 ;; c2" := (bind c1 (fun x => c2))
(at level 61, c1 at next level, right associativity).
Open Scope string.
(* A subtraction function with an error *)
Definition sub (n m:nat):M nat:=
match Compare_dec.le_lt_dec n m with
right _ => inl (n - m)
| left _ => inr ("the RHS is too big")
end.
Eval compute in (sub 2 5).
(* The numbers [1...n] *)
Definition safe_seq (n:nat): M (list nat):=
match n with
O => inr ("We want a non-empty list")%string
| S n => inl (seq 1 (S n)) end.
Eval compute in (inr "problem"%string >>= safe_seq).
Eval compute in (l<- safe_seq 0 ;; ret l).
Eval compute in (seq 1 2).
Eval compute in
(l<- safe_seq 2 ;; ret ((map S) l)).
Definition hd : list nat-> M (list nat).
intros [| h t].
exact (inr "list is empty"%string).
exact (inl [h]).
Show Proof.
Defined.
Check (safe_seq 4).
Eval compute in (l<-safe_seq 4 ;; (hd l)).
Eval compute in (l<-safe_seq 4 ;;
k<- (hd l) ;;
hd k).
Eval compute in (safe_seq 4 >>= hd >>= hd).
Eval compute in (safe_seq 4 >>= hd ).
End Error.
(** Reader *)
Module Reader.
(** An environment: *)
Variable E:Type.
Definition reader T := E -> T.
Definition ret {X}:X-> reader X := fun x => fun e => x.
Definition bind {A B} (m : reader A) (f : A -> reader B): reader B:=
fun e => f (m e) e.
(** The bound value and the function get access to the same shared environment for the computation.
Note: These definitions coincide with the definitions of the K and S combinators in the SKI-calculus. *)
Local Instance reader_monad: Monad (@ret) (@bind). Admitted.
End Reader.
(** Continuation Passing style
CPS is one of the compiler optimization passes. *)
Module CPS.
Variable S:Type.
Definition M X:= (X->S)->S.
Definition ret {X}: X -> M X := fun x => (fun f => (f x)).
Definition bind {A B} (m : M A) (f : A -> M B): M B:=
fun g => m (fun a : A => f a g).
Local Instance CPS_monad: Monad (@ret) (@bind). Admitted.
(** ret: we have a value and wait to receive a computation takes this value.
bind: given a value and some function f, we compose the computation of f
with the rest of the computation given in g
and then apply it to the starting value x. *)
End CPS.
(** Exercise (optional) prove that these satisfy the monad laws. *)
(** We have seen many examples of monads:
* partiality,
* error handling,
* statefulness,
* non-determinism
* continuations
* reader
* list *)
(** The week after that we will see the probability monad *)
(* Here is a Coq library used for verifying haskell programs.
Monads are an important part of it.
These monads extract to monads in haskell, where the monadic programs can be run. *)
(* https://github.com/jwiegley/coq-haskell
https://github.com/coq-community/coq-ext-lib/blob/master/theories/Structures/Monad.v
*)