Skip to content

Commit 583cf30

Browse files
committed
Example: add monad file
1 parent 5f41787 commit 583cf30

File tree

1 file changed

+147
-0
lines changed

1 file changed

+147
-0
lines changed

monad_examples.v

+147
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
Require Import Coq.Strings.String.
2+
Require Import Coq.ZArith.ZArith.
3+
4+
Open Scope Z.
5+
6+
(* pure function example *)
7+
Definition plus_two (n : nat) : nat :=
8+
n + 2.
9+
10+
(* Functions with side effects: not possible in Coq but possible in other programming languages *)
11+
(* side-effect 1 : input-output operation *)
12+
(* Definition get_current_time_in_seconds : nat :=
13+
Time.get_current_time (). *)
14+
15+
(* side-effect 2: modify a state *)
16+
17+
(* Definition global_bar := 2.
18+
19+
Definition increment () : unit :=
20+
global_var += 1.
21+
22+
(* side-effect 3: propagate an error *)
23+
24+
(* side-effect: errors *)
25+
Definition divide_by_zero n :=
26+
n / 0. *)
27+
28+
(* To represent side effects in purely functional language: monads *)
29+
30+
(* Monads *)
31+
(* M A: monadic type: "computations that returns a value of type A" *)
32+
(* Two primitive operations *)
33+
(* return/pure: A -> M A : to convert pure operations to monadic operations *)
34+
(* bind: M A -> (A -> M B) -> M B : to sequence two monadic operations *)
35+
36+
(* Example of monads: *)
37+
(* Error *)
38+
Inductive ErrorMonad (A : Set) : Set :=
39+
| Success (value : A)
40+
| Error (message : string).
41+
Arguments Success {_}.
42+
Arguments Error {_}.
43+
44+
Definition pure_error_monad {A : Set} (value : A) : ErrorMonad A :=
45+
Success value.
46+
47+
Definition bind_error_monad {A B : Set} (first : ErrorMonad A) (second : A -> ErrorMonad B) : ErrorMonad B :=
48+
match first with
49+
| Success value => second value
50+
| Error message => Error message
51+
end.
52+
53+
Definition division_by_something (z1 z2 : Z) : ErrorMonad Z :=
54+
if z2 =? 0 then
55+
Error "division by zero"
56+
else
57+
Success (z1 / z2).
58+
59+
(* a1 / a2 + b1 / b2 *)
60+
Definition sum_of_two_divisions (a1 a2 b1 b2 : Z) : ErrorMonad Z :=
61+
bind_error_monad (division_by_something a1 a2) (fun x =>
62+
bind_error_monad (division_by_something b1 b2) (fun y =>
63+
pure_error_monad (x + y)%Z
64+
)
65+
).
66+
67+
Compute sum_of_two_divisions 4 3 2 1. (* => Success value *)
68+
Compute sum_of_two_divisions 4 0 2 1. (* => Error message *)
69+
70+
(* IO example *)
71+
Inductive PossibleIO : Set -> Set :=
72+
| GetTime : PossibleIO Z
73+
| ReadFile (filename : string) : PossibleIO (option string)
74+
| WriteFile (filename : string) (new_content : string) : PossibleIO bool.
75+
76+
(* With the IO monad, everything is data, we do not actually compute anything *)
77+
Inductive IO (A : Set) : Set :=
78+
| Pure (value : A)
79+
| Impure (io : PossibleIO A)
80+
| Bind {B : Set} (first : IO B) (second : B -> IO A).
81+
Arguments Pure {_}.
82+
Arguments Impure {_}.
83+
Arguments Bind {_ _}.
84+
85+
Definition get_time : IO Z :=
86+
Impure GetTime.
87+
88+
Definition read_file_and_return_duration (filename : string) : IO Z :=
89+
Bind get_time (fun initial_time =>
90+
Bind (Impure (ReadFile filename)) (fun _ =>
91+
Bind get_time (fun final_time =>
92+
Pure (final_time - initial_time)
93+
))).
94+
95+
(* The output is very verbose, as this is the code above that is printed expanding all the
96+
definitions, but nothing is computed. *)
97+
Compute read_file_and_return_duration "example.txt".
98+
99+
(* We can still specify things about programs in the IO monands, even if we cannot compute. Here is
100+
an example of a simple function that checks if a computation is actually just a pure value
101+
without any IO. *)
102+
Definition is_IO_without_IO {A : Set} (computation : IO A) : bool :=
103+
match computation with
104+
| Pure _ => true
105+
| _ => false
106+
end.
107+
108+
Compute is_IO_without_IO get_time. (* => false *)
109+
Compute is_IO_without_IO (Pure (1 + 2)%Z). (* => true *)
110+
111+
(* State monad *)
112+
(* The idea is to add the input state as an additional parameter to the functions, and the output
113+
state as an addition result *)
114+
Definition StateMonad (State : Set) (A : Set) : Set :=
115+
State (* input *) -> A * State (* output *).
116+
117+
(* We assume that our state is a global variable of type [Z] *)
118+
Definition State : Set :=
119+
Z.
120+
121+
(* We do not change the state in the pure operator *)
122+
Definition pure_state {A : Set} (value : A) : StateMonad State A :=
123+
fun state => (value, state).
124+
125+
(* We propagate the updated state in the bind operator *)
126+
Definition bind_state {A B : Set} (first : StateMonad State A) (second : A -> StateMonad State B) : StateMonad State B :=
127+
fun state =>
128+
let (value, new_state) := first state in
129+
second value new_state.
130+
131+
(* We can now define the increment operation *)
132+
Definition increment : StateMonad State unit :=
133+
fun state => (tt, state + 1).
134+
135+
(* We can apply the increment three times *)
136+
Definition increment_three_times : StateMonad State unit :=
137+
bind_state increment (fun _ =>
138+
bind_state increment (fun _ =>
139+
bind_state increment (fun _ =>
140+
pure_state tt
141+
))).
142+
143+
Definition initial_state : State :=
144+
12.
145+
146+
Compute increment initial_state. (* => (tt, 13 ) *)
147+
Compute increment_three_times initial_state. (* => (tt, 15) *)

0 commit comments

Comments
 (0)