-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patherror_monad.ml
More file actions
487 lines (415 loc) · 13.6 KB
/
error_monad.ml
File metadata and controls
487 lines (415 loc) · 13.6 KB
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
(**************************************************************************)
(* *)
(* Copyright (c) 2014 - 2016. *)
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
(* *)
(* All rights reserved. No warranty, explicit or implicit, provided. *)
(* *)
(**************************************************************************)
(* Tezos Protocol Implementation - Error Monad *)
(*-- Error classification ----------------------------------------------------*)
type error_category = [ `Branch | `Temporary | `Permanent ]
type 'err full_error_category =
[ error_category | `Wrapped of 'err -> error_category ]
(* HACK: forward reference from [Data_encoding_ezjsonm] *)
let json_to_string = ref (fun _ -> "")
let json_pp id encoding ppf x =
Format.pp_print_string ppf @@
!json_to_string @@
let encoding =
Data_encoding.(merge_objs (obj1 (req "id" string)) encoding) in
Data_encoding.Json.construct encoding (id, x)
module Make() = struct
type error = ..
(* the toplevel store for error kinds *)
type error_kind =
Error_kind :
{ id: string ;
from_error: error -> 'err option ;
category: 'err full_error_category ;
encoding_case: error Data_encoding.case ;
pp: Format.formatter -> 'err -> unit ; } ->
error_kind
type registred_errors = error_kind list
let error_kinds
: error_kind list ref
= ref []
let error_encoding_cache = ref None
let string_of_category = function
| `Permanent -> "permanent"
| `Temporary -> "temporary"
| `Branch -> "branch"
| `Wrapped _ -> "wrapped"
let raw_register_error_kind
category ~id:name ~title ~description ?pp
encoding from_error to_error =
if List.exists
(fun (Error_kind { id }) -> name = id)
!error_kinds then
invalid_arg
(Printf.sprintf
"register_error_kind: duplicate error name: %s" name) ;
let encoding_case =
let open Data_encoding in
case
(describe ~title ~description @@
conv (fun x -> (((), ()), x)) (fun (((),()), x) -> x) @@
merge_objs
(obj2
(req "kind" (constant (string_of_category category)))
(req "id" (constant name)))
encoding)
from_error to_error in
error_encoding_cache := None ;
error_kinds :=
Error_kind { id = name ;
category ;
from_error ;
encoding_case ;
pp = Utils.unopt ~default:(json_pp name encoding) pp } :: !error_kinds
let register_wrapped_error_kind
category ~id ~title ~description ?pp
encoding from_error to_error =
raw_register_error_kind
(`Wrapped category)
~id ~title ~description ?pp
encoding from_error to_error
let register_error_kind
category ~id ~title ~description ?pp
encoding from_error to_error =
raw_register_error_kind
(category :> _ full_error_category)
~id ~title ~description ?pp
encoding from_error to_error
let error_encoding () =
match !error_encoding_cache with
| None ->
let cases =
List.map
(fun (Error_kind { encoding_case }) -> encoding_case )
!error_kinds in
let json_encoding = Data_encoding.union cases in
let encoding =
Data_encoding.splitted
~json:json_encoding
~binary:
(Data_encoding.conv
(Data_encoding.Json.construct json_encoding)
(Data_encoding.Json.destruct json_encoding)
Data_encoding.json) in
error_encoding_cache := Some encoding ;
encoding
| Some encoding -> encoding
let json_of_error error =
Data_encoding.Json.(construct (error_encoding ())) error
let error_of_json json =
Data_encoding.Json.(destruct (error_encoding ())) json
let classify_error error =
let rec find e = function
| [] -> `Temporary
(* assert false (\* See "Generic error" *\) *)
| Error_kind { from_error ; category } :: rest ->
match from_error e with
| Some x -> begin
match category with
| `Wrapped f -> f x
| #error_category as x -> x
end
| None -> find e rest in
find error !error_kinds
let classify_errors errors =
List.fold_left
(fun r e -> match r, classify_error e with
| `Permanent, _ | _, `Permanent -> `Permanent
| `Branch, _ | _, `Branch -> `Branch
| `Temporary, `Temporary -> `Temporary)
`Temporary errors
let pp ppf error =
let rec find = function
| [] -> assert false (* See "Generic error" *)
| Error_kind { from_error ; pp } :: errors ->
match from_error error with
| None -> find errors
| Some x -> pp ppf x in
find !error_kinds
let registred_errors () = !error_kinds
let pp_print_error ppf errors =
match errors with
| [] ->
Format.fprintf ppf "Unknown error@."
| [error] ->
Format.fprintf ppf "@[<v 2>Error:@ %a@]@." pp error
| errors ->
Format.fprintf ppf "@[<v 2>Error, dumping error stack:@,%a@]@."
(Format.pp_print_list pp)
(List.rev errors)
(*-- Monad definition --------------------------------------------------------*)
let (>>=) = Lwt.(>>=)
type 'a tzresult = ('a, error list) result
let result_encoding t_encoding =
let open Data_encoding in
let errors_encoding =
describe ~title: "An erroneous result" @@
obj1 (req "error" (list (error_encoding ()))) in
let t_encoding =
describe ~title: "A successful result" @@
obj1 (req "result" t_encoding) in
union
~tag_size:`Uint8
[ case ~tag:0 t_encoding
(function Ok x -> Some x | _ -> None)
(function res -> Ok res) ;
case ~tag:1 errors_encoding
(function Error x -> Some x | _ -> None)
(fun errs -> Error errs) ]
let return v = Lwt.return (Ok v)
let error s = Error [ s ]
let ok v = Ok v
let fail s = Lwt.return (Error [ s ])
let (>>?) v f =
match v with
| Error _ as err -> err
| Ok v -> f v
let (>>=?) v f =
v >>= function
| Error e as err ->
let _ = pp_print_error Format.err_formatter e in
Lwt.return err
| Ok v -> f v
let (>>|?) v f = v >>=? fun v -> Lwt.return (Ok (f v))
let (>|=) = Lwt.(>|=)
let (>|?) v f = v >>? fun v -> Ok (f v)
let rec map_s f l =
match l with
| [] -> return []
| h :: t ->
f h >>=? fun rh ->
map_s f t >>=? fun rt ->
return (rh :: rt)
let mapi_s f l =
let rec mapi_s f i l =
match l with
| [] -> return []
| h :: t ->
f i h >>=? fun rh ->
mapi_s f (i+1) t >>=? fun rt ->
return (rh :: rt)
in
mapi_s f 0 l
let rec map_p f l =
match l with
| [] ->
return []
| x :: l ->
let tx = f x and tl = map_p f l in
tx >>= fun x ->
tl >>= fun l ->
match x, l with
| Ok x, Ok l -> Lwt.return (Ok (x :: l))
| Error exn1, Error exn2 -> Lwt.return (Error (exn1 @ exn2))
| Ok _, Error exn
| Error exn, Ok _ -> Lwt.return (Error exn)
let mapi_p f l =
let rec mapi_p f i l =
match l with
| [] ->
return []
| x :: l ->
let tx = f i x and tl = mapi_p f (i+1) l in
tx >>= fun x ->
tl >>= fun l ->
match x, l with
| Ok x, Ok l -> Lwt.return (Ok (x :: l))
| Error exn1, Error exn2 -> Lwt.return (Error (exn1 @ exn2))
| Ok _, Error exn
| Error exn, Ok _ -> Lwt.return (Error exn) in
mapi_p f 0 l
let rec map2_s f l1 l2 =
match l1, l2 with
| [], [] -> return []
| _ :: _, [] | [], _ :: _ -> invalid_arg "Error_monad.map2_s"
| h1 :: t1, h2 :: t2 ->
f h1 h2 >>=? fun rh ->
map2_s f t1 t2 >>=? fun rt ->
return (rh :: rt)
let rec map2 f l1 l2 =
match l1, l2 with
| [], [] -> Ok []
| _ :: _, [] | [], _ :: _ -> invalid_arg "Error_monad.map2"
| h1 :: t1, h2 :: t2 ->
f h1 h2 >>? fun rh ->
map2 f t1 t2 >>? fun rt ->
Ok (rh :: rt)
let rec filter_map_s f l =
match l with
| [] -> return []
| h :: t ->
f h >>=? function
| None -> filter_map_s f t
| Some rh ->
filter_map_s f t >>=? fun rt ->
return (rh :: rt)
let rec filter_map_p f l =
match l with
| [] -> return []
| h :: t ->
let th = f h
and tt = filter_map_s f t in
th >>=? function
| None -> tt
| Some rh ->
tt >>=? fun rt ->
return (rh :: rt)
let rec iter_s f l =
match l with
| [] -> return ()
| h :: t ->
f h >>=? fun () ->
iter_s f t
let rec iter_p f l =
match l with
| [] -> return ()
| x :: l ->
let tx = f x and tl = iter_p f l in
tx >>= fun tx_res ->
tl >>= fun tl_res ->
match tx_res, tl_res with
| Ok (), Ok () -> Lwt.return (Ok ())
| Error exn1, Error exn2 -> Lwt.return (Error (exn1 @ exn2))
| Ok (), Error exn
| Error exn, Ok () -> Lwt.return (Error exn)
let rec fold_left_s f init l =
match l with
| [] -> return init
| h :: t ->
f init h >>=? fun acc ->
fold_left_s f acc t
let rec fold_right_s f l init =
match l with
| [] -> return init
| h :: t ->
fold_right_s f t init >>=? fun acc ->
f h acc
let rec join = function
| [] -> return ()
| t :: ts ->
t >>= function
| Error _ as err ->
join ts >>=? fun () ->
Lwt.return err
| Ok () ->
join ts
let record_trace err result =
match result with
| Ok _ as res -> res
| Error errs -> Error (err :: errs)
let trace err f =
f >>= function
| Error errs -> Lwt.return (Error (err :: errs))
| ok -> Lwt.return ok
let fail_unless cond exn =
if cond then return () else fail exn
let fail_when cond exn =
if cond then fail exn else return ()
let unless cond f =
if cond then return () else f ()
let _when cond f =
if cond then f () else return ()
type error += Unclassified of string
let () =
let id = "" in
let category = `Temporary in
let to_error msg = Unclassified msg in
let from_error = function
| Unclassified msg -> Some msg
| error ->
let msg = Obj.(extension_name @@ extension_constructor error) in
Some ("Unclassified error: " ^ msg ^ ". Was the error registered?") in
let title = "Generic error" in
let description = "An unclassified error" in
let encoding_case =
let open Data_encoding in
case
(describe ~title ~description @@
conv (fun x -> ((), x)) (fun ((), x) -> x) @@
(obj2
(req "kind" (constant "generic"))
(req "error" string)))
from_error to_error in
let pp = Format.pp_print_string in
error_kinds :=
Error_kind { id; from_error ; category; encoding_case ; pp } :: !error_kinds
type error += Assert_error of string * string
let () =
let id = "" in
let category = `Permanent in
let to_error (loc, msg) = Assert_error (loc, msg) in
let from_error = function
| Assert_error (loc, msg) -> Some (loc, msg)
| _ -> None in
let title = "Assertion error" in
let description = "An fatal assertion" in
let encoding_case =
let open Data_encoding in
case
(describe ~title ~description @@
conv (fun (x, y) -> ((), x, y)) (fun ((), x, y) -> (x, y)) @@
(obj3
(req "kind" (constant "assertion"))
(req "location" string)
(req "error" string)))
from_error to_error in
let pp ppf (loc, msg) =
Format.fprintf ppf
"Assert failure (%s)%s"
loc
(if msg = "" then "." else ": " ^ msg) in
error_kinds :=
Error_kind { id; from_error ; category; encoding_case ; pp } :: !error_kinds
let _assert b loc fmt =
if b then
Format.ikfprintf (fun _ -> return ()) Format.str_formatter fmt
else
Format.kasprintf (fun msg -> fail (Assert_error (loc, msg))) fmt
let protect ~on_error t =
t >>= function
| Ok res -> return res
| Error err -> on_error err
end
include Make()
let generic_error fmt =
Format.kasprintf (fun s -> error (Unclassified s)) fmt
let failwith fmt =
Format.kasprintf (fun s -> fail (Unclassified s)) fmt
type error += Exn of exn
let error s = Error [ s ]
let error_exn s = Error [ Exn s ]
let trace_exn exn f = trace (Exn exn) f
let generic_trace fmt =
Format.kasprintf (fun str -> trace_exn (Failure str)) fmt
let record_trace_exn exn f = record_trace (Exn exn) f
let failure fmt =
Format.kasprintf (fun str -> Exn (Failure str)) fmt
let protect ?on_error t =
Lwt.catch t (fun exn -> fail (Exn exn)) >>= function
| Ok res -> return res
| Error err ->
match on_error with
| Some f -> f err
| None -> Lwt.return (Error err)
let pp_exn ppf exn = pp ppf (Exn exn)
let () =
register_error_kind
`Temporary
~id:"failure"
~title:"Generic error"
~description:"Unclassified error"
~pp:Format.pp_print_string
Data_encoding.(obj1 (req "msg" string))
(function
| Exn (Failure msg) -> Some msg
| Exn (Unix.Unix_error (err, fn, _)) ->
Some ("Unix error in " ^ fn ^ ": " ^ Unix.error_message err)
| Exn exn -> Some (Printexc.to_string exn)
| _ -> None)
(fun msg -> Exn (Failure msg))