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