-
-
Notifications
You must be signed in to change notification settings - Fork 22
/
FreeMonadInterpreter.re
141 lines (119 loc) · 3.02 KB
/
FreeMonadInterpreter.re
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
/**
Sample Free Monad module.
A more elegant approach to defining this can be seen in:
https://www.cl.cam.ac.uk/~jdy22/papers/lightweight-higher-kinded-polymorphism.pdf
*/
module FreeM = {
module type FUNCTOR = {
type t('a);
let map: ('a => 'b, t('a)) => t('b);
};
module type FREE = {
type f('a);
type t('a) =
| Free(f(t('a)))
| Return('a);
let return: 'a => t('a);
let (>>=): (t('a), 'a => t('b)) => t('b);
let lift: f('a) => t('a);
};
module Make = (M: FUNCTOR) : (FREE with type f('a) = M.t('a)) => {
type f('a) = M.t('a);
type t('a) =
| Free(f(t('a)))
| Return('a);
let return = x => Return(x);
let rec (>>=) = (x, f) => {
switch (x) {
| Free(y) => Free(M.map(z => z >>= f, y))
| Return(y) => f(y)
};
};
let lift = x => Free(M.map(return, x));
};
};
/**
Our Storage DSL
*/
module Storage = {
type t('next) =
| Get(string, string => 'next)
| Put(string, string, 'next)
| End;
let map = f =>
fun
| Get(name, k) => Get(name, x => x |> k |> f)
| Put(name, value, next) => Put(name, value, f(next))
| End => End;
module Free: FreeM.FREE with type f('a) = t('a) =
FreeM.Make({
type f('a) = t('a);
let map = map;
});
let get = k => Free.lift(Get(k, x => x));
let put = (k, v) => Free.lift(Put(k, v, ()));
let end_ = () => Free.lift(End);
};
/**
A printer module that given a Storage program will print out the commands
that will be carried out.
*/
module Storage_printer = {
open Storage;
let rec run: Storage.Free.t('a) => unit =
fun
| Free(Get(key, k)) => {
Format.print_string(Format.asprintf("Get(%s)", key));
Format.force_newline();
"value" |> k |> run;
}
| Free(Put(key, value, k)) => {
Format.print_string(Format.asprintf("Put(%s, %s)", key, value));
Format.force_newline();
run(k);
}
| Free(End)
| Return(_) => ();
};
/**
An in-memory version of the storage, ideal for testing purposes.
*/
module In_memory_storage: {let run: Storage.Free.t('a) => unit;} = {
open Storage;
module DB =
Hashtbl.Make({
type t = string;
let equal = String.equal;
let hash = Hashtbl.hash;
});
let db: DB.t(string) = DB.create(1024);
let print = () =>
DB.fold((k, v, acc) => [k ++ " = " ++ v, ...acc], db, [])
|> List.iter(s => Printf.printf("%s\n", s));
let rec run: Storage.Free.t('a) => unit =
fun
| Free(Get(key, k)) => DB.find(db, key) |> k |> run
| Free(Put(key, value, k)) => {
DB.add(db, key, value);
run(k);
}
| Free(End)
| Return(_) => ();
};
/**
A sample program
*/
module Test = {
open Storage;
open Storage.Free;
let _ = {
let program =
put("some key", "some value!")
>>= (_ => get("some key"))
>>= (value => put(value, "another value!"))
>>= (_ => get("some value!"));
program |> Storage_printer.run;
program |> In_memory_storage.run;
In_memory_storage.print();
};
};