Mavryk_error_monad.TzTrace
A trace
is a stack of error
s. It is implemented as an error list
but such a list MUST NEVER be empty.
It is implemented as a concrete error list
for backwards compatibility but future improvements might modify the type or render the type abstract.
include Sig.TRACE with type 'err trace = 'err list
The trace
type (included as part of the Mavryk_lwt_result_stdlib.Lwtreslib.TRACE
module is abstract in this interface but it is made concrete in the instantiated error monad (see error_monad.mli
).
The idea of abstracting the trace is so that it can evolve more easily. Eventually, we can make the trace abstract in the instantiated error monad, we can have different notions of traces for the protocol and the shell, etc.
include Mavryk_lwt_result_stdlib.Lwtreslib.TRACE
with type 'err trace = 'err list
trace
are the errors as received from the monad. In other words, trace
is the type of values that are seen when matching on Error _
to, say, recover.
The types 'error
and 'error trace
are kept separate (although they can be equal) to support cases such as the following:
trace
are richer than error
, such as by including a timestamp, a filename, or some other such metadata.trace
is a private
type or an abstract
type and error
is the type of argument to the functions that construct the private/abstract trace
.trace
is a collection of error
and additional functions (not required by this library) allow additional manipulation. E.g., in the case of Mavryk: errors are built into traces that can be grown.There is some leeway about what traces are, what information they carry, etc. Beyond this leeway, Lwtreslib is opinionated about traces. Specifically, Lwtreslib has a notion of sequential and parallel composition. A trace can be either of the following.
make
.cons
.conp
.val make : 'error -> 'error trace
make e
is a trace made of one single error.
cons e t
is a trace made of the error e
composed sequentially with the trace t
.
Typically, this is used to give context to a low-level error.
let query key =
let open Lwt_result_syntax in
let* c = connect_to_server () in
let* r = send_query_over_connection c key in
let* () = check_response r in
return r
let query key =
let open Lwt_syntax in
let+ r = query_key in
match r with
| Ok r -> Ok r
| Error trace -> Error (cons `Query_failure trace)
val cons_list : 'error -> 'error list -> 'error trace
cons_list error errors
is the sequential composition of all the errors passed as parameters. It is equivalent to folding cons
over List.rev (error :: errors)
but more efficient.
Note that error
and errors
are separated as parameters to enforce that empty traces cannot be constructed. The recommended use is:
match a_bunch_of_errors with
| [] -> Ok () (* or something else depending on the context *)
| error :: errors -> Error (cons_list error errors)
conp t1 t2
is a trace made of the traces t1
and t2
composed concurrently.
conp_list trace traces
is the parallel composition of all the traces passed as parameters. It is equivalent to List.fold_left conp trace traces
but more efficient.
Note that trace
and traces
are separated as parameters to enforce that empty traces cannot be constructed. The recommended use is:
match a_bunch_of_traces with
| [] -> Ok () (* or something else depending on the context *)
| trace :: traces -> Error (conp_list trace traces)
Note that the Lwtreslib's library does not require it, but it is recommended that you also make, for your own use, a pretty-printing function as well as some introspection functions.
One possible extension can be found in examples/traces/traces.ml
.
val pp_print :
(Stdlib.Format.formatter -> 'err -> unit) ->
Stdlib.Format.formatter ->
'err trace ->
unit
pp_print
pretty-prints a trace of errors
val pp_print_top :
(Stdlib.Format.formatter -> 'err -> unit) ->
Stdlib.Format.formatter ->
'err trace ->
unit
pp_print_top
pretty-prints the top errors of the trace
val encoding : 'error Data_encoding.t -> 'error trace Data_encoding.t
val fold : ('a -> 'error -> 'a) -> 'a -> 'error trace -> 'a
fold f init trace
traverses the trace (in an unspecified manner) so that init
is folded over each of the error within trace
by f
. Typical use is to find the worst error, to check for the presence of a given error, etc.