123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518(*****************************************************************************)(* *)(* Open Source License *)(* Copyright (c) 2020 Nomadic Labs <contact@nomadic-labs.com> *)(* *)(* Permission is hereby granted, free of charge, to any person obtaining a *)(* copy of this software and associated documentation files (the "Software"),*)(* to deal in the Software without restriction, including without limitation *)(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *)(* and/or sell copies of the Software, and to permit persons to whom the *)(* Software is furnished to do so, subject to the following conditions: *)(* *)(* The above copyright notice and this permission notice shall be included *)(* in all copies or substantial portions of the Software. *)(* *)(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *)(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *)(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *)(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *)(* DEALINGS IN THE SOFTWARE. *)(* *)(*****************************************************************************)(** {1 Lwt, result, and Lwt-result monad operators}
This module provides the necessary functions and operators to use Lwt,
Result and Lwt-Result as monads.
{2 Basics}
The three, tiered monads have each their own syntax module with
{ul
{li a [return] function,}
{li preallocated [return_unit], [return_none], etc. values,}
{li [let*] and [let+] bindind operators.}
}
In addition, the {!Lwt_syntax} module has [and*] and [and+] binding
operators to allow concurrent evaluation of two or more promises, and the
{!Result_syntax} and {!Lwt_result_syntax} have [fail] functions to
error-out.
{2 Joins}
The {!Lwt_syntax.join} function takes a list of promises [ps] and returns a
single promise [p] that resolves with [()] when all the promises of [ps]
have resolved.
The {!Lwt_syntax.all} function takes a list of promises [ps] and returns a
single promise [p] that resolves when all the promises of [ps] have
resolved. The value [p] resolves to is the list of values the promises of
[ps] resolve to. The order is preserved.
The {!Lwt_syntax.both} function takes two promises [p1] and [p2] and returns
a single promise [p] that resolves when both promises [p1] and [p2] have
resolved. The value [p] resolves to is the tuple of values the promises [p1]
and [p2] resolve to.
These Lwt-joining functions have a best-effort semantic: they only resolve
once all the underlying promises have resolved.
The {!Result_syntax} variants are equivalent for the result monad: the final
result is [Ok] if all the underlying results are [Ok].
The {!Lwt_result_syntax} variants are equivalent for the Lwt-result monad:
the final promise resolves to [Ok] if all the underlying promise resolve to
[Ok].
{2 Lifting}
Finally, the {!Lwt_result_syntax} module includes two facilities for lifting
values from the more specilaised Lwt-only and Result-only monads.
[let*!] binds a plain Lwt promise into an Lwt-Result promise.
{[
let open Lwt_result_syntax in
let*! x = f a b c in
…
]}
[let*?] binds a plain result into an Lwt-Result promise.
{[
let open Lwt_result_syntax in
let*? y = f u v w in
…
]}
In the cases where performance is not a grave concern, it is also possible to
use [Lwt_result.ok] to lift Lwt-only expressions and [Lwt.return] to lift
result-only expressions.
More details on the matter within the documentation of
{!Lwt_result_syntax.( let*! )} and {!Lwt_result_syntax.( let*? )}
themselves. *)moduletypeS=sig(** {1 The tower of monads} *)(** {2 The Lwt monad: for concurrency} *)(** Syntax module for Lwt. This is intended to be opened locally in functions
which use Lwt for control-flow. Within the scope of this module, the code
can include binding operators, leading to a [let]-style syntax.
See also {!Lwt} and {!Lwt.Syntax} *)moduleLwt_syntax:sig(** [return x] is an Lwt promise that is already resolved to [x]. [return]
is an alias for {!Lwt.return}. *)valreturn:'a->'aLwt.t(** [return_unit] is an Lwt promise that is already resolved to [()]. It is
an alias for [Lwt.return_unit]. *)valreturn_unit:unitLwt.t(** [return_none] is an Lwt promise that is already resolved to [None]. It
is an alias for [Lwt.return_none]. *)valreturn_none:_optionLwt.t(** [return_nil] is an Lwt promise that is already resolved to [[]]. It is
an alias for [Lwt.return_nil]. *)valreturn_nil:_listLwt.t(** [return_true] is an Lwt promise that is already resolved to [true]. It is
an alias for [Lwt.return_true]. *)valreturn_true:boolLwt.t(** [return_false] is an Lwt promise that is already resolved to [false]. It is
an alias for [Lwt.return_false]. *)valreturn_false:boolLwt.t(** [return_some x] is an Lwt promise that is already resolved to [Some x].
[return_some] is an alias for [Lwt.return_some]. *)valreturn_some:'a->'aoptionLwt.t(** [return_ok x] is an Lwt promise that is already resolved to [Ok x].
[return_ok] is an alias for [Lwt.return_ok]. *)valreturn_ok:'a->('a,_)resultLwt.t(** [return_error x] is an Lwt promise that is already resolved to [Error x].
[return_error] is an alias for [Lwt.return_error]. *)valreturn_error:'e->(_,'e)resultLwt.t(** The following [return_ok_*] functions are intended to be used within the
scope of [Lwt_syntax] when returning results compatible with
[Lwt_result_syntax]. *)(** [return_ok_unit] is an Lwt promise that is already resolved to [Ok ()]. *)valreturn_ok_unit:(unit,'e)resultLwt.t(** [return_ok_true] is an Lwt promise that is already resolved to [Ok true]. *)valreturn_ok_true:(bool,'e)resultLwt.t(** [return_ok_false] is an Lwt promise that is already resolved to [Ok false]. *)valreturn_ok_false:(bool,'e)resultLwt.t(** [return_ok_none] is an Lwt promise that is already resolved to [Ok None]. *)valreturn_ok_none:('aoption,'e)resultLwt.t(** [return_ok_nil] is an Lwt promise that is already resolved to [Ok []]. *)valreturn_ok_nil:('alist,'e)resultLwt.t(** [let*] is a binding operator alias for {!Lwt.bind} and {!Lwt.( >>= )}. *)val(let*):'aLwt.t->('a->'bLwt.t)->'bLwt.t(** [and*] is a binding operator alias for {!Lwt.both} and {!Lwt.( <&> )}. *)val(and*):'aLwt.t->'bLwt.t->('a*'b)Lwt.t(** [let+] is a binding operator alias for {!Lwt.map} and {!Lwt.( >|= )}. *)val(let+):'aLwt.t->('a->'b)->'bLwt.t(** [and+] is a binding operator alias for {!Lwt.both} and {!Lwt.( <&> )}. *)val(and+):'aLwt.t->'bLwt.t->('a*'b)Lwt.t(** [join] is the joining of concurrent unit values (it is [Lwt.join]). *)valjoin:unitLwt.tlist->unitLwt.t(** [all] is the joining of concurrent non-unit values (it is [Lwt.all]). *)valall:'aLwt.tlist->'alistLwt.t(** [both] is the joining of two concurrent non-unit values (it is [Lwt.both]). *)valboth:'aLwt.t->'bLwt.t->('a*'b)Lwt.tend(** {2 The Option monad: for optional data} *)(** Syntax module for Option. This is intended to be opened locally in
functions which use [option] for control-flow. Within the scope of this
module, the code can include binding operators, leading to a [let]-style
syntax.
See also {!Option} *)moduleOption_syntax:sig(** [return x] is [Some x]. *)valreturn:'a->'aoption(** [fail] is [None]. It is also an alias for [Option.none]. *)valfail:'aoption(** [return_unit] is [Some ()]. *)valreturn_unit:unitoption(** [return_nil] is [Some []]. *)valreturn_nil:'alistoption(** [return_true] is [Some true]. *)valreturn_true:booloption(** [return_false] is [Some false]. *)valreturn_false:booloption(** Note that we do not provide [return_some] nor [return_none]. Both of
these functions are possible but somewhat confusing and rarely useful in
practice. If you need to carry [option]s within a Option-monad
computation (yielding to values of the type
['a option option]), you need to do so by hand:
[return (Some …)] and [return None]. *)(** [let*] is a binding operator alias for {!Option.bind}. *)val(let*):'aoption->('a->'boption)->'boption(** [and*] is a binding operator alias for [both]. *)val(and*):'aoption->'boption->('a*'b)option(** [let+] is a binding operator alias for {!Option.map}. *)val(let+):'aoption->('a->'b)->'boption(** [and+] is a binding operator alias for [both]. *)val(and+):'aoption->'boption->('a*'b)option(** [both] is the joining of two optional non-unit values. [both a b] is
[Some (x, y)] iff [a] is [Some x] and [b] is [Some y]. *)valboth:'aoption->'boption->('a*'b)optionend(** {2 The (generic) Result monad: for success/failure} *)(** Syntax module for Result. This is intended to be opened locally in
functions which use [result] for control-flow. Within the scope of this
module, the code can include binding operators, leading to a [let]-style
syntax.
See also {!Result} *)moduleResult_syntax:sig(** [return x] is [Ok x]. *)valreturn:'a->('a,'e)result(** [return_unit] is [Ok ()]. *)valreturn_unit:(unit,'e)result(** [return_none] is [Ok None]. *)valreturn_none:('aoption,'e)result(** [return_some x] is [Ok (Some x)]. *)valreturn_some:'a->('aoption,'e)result(** [return_nil] is [Ok []]. *)valreturn_nil:('alist,'e)result(** [return_true] is [Ok true]. *)valreturn_true:(bool,'e)result(** [return_false] is [Ok false]. *)valreturn_false:(bool,'e)result(** Note that we do not provide [return_ok] nor [return_error]. Both of
these functions are possible but somewhat confusing and rarely useful in
practice. If you need to carry [result]s within a Result-monad
computation (yielding to values of the type
[(('a, 'e) result, 'e) result]), you need to do so by hand:
[return (Ok …)] and [return (Error …)]. *)(** [fail e] is [Error e]. It is also an alias for [error]. *)valfail:'e->('a,'e)result(** [let*] is a binding operator alias for {!Result.bind} and [>>?]. *)val(let*):('a,'e)result->('a->('b,'e)result)->('b,'e)result(** [let+] is a binding operator alias for {!Result.map} and [>|?]. *)val(let+):('a,'e)result->('a->'b)->('b,'e)result(** Note that we do not provide [and*] nor [and+]. Both of these are
possible but their type is unsatisfying because the errors do not
compose well. You can use [both] (below) if need be. *)(** [join] is the joining of success/failure unit values. *)valjoin:(unit,'e)resultlist->(unit,'elist)result(** [all] is the joining of success/failure non-unit values. *)valall:('a,'e)resultlist->('alist,'elist)result(** [both] is the joining of two success/failure non-unit values. *)valboth:('a,'e)result->('b,'e)result->('a*'b,'elist)resultend(** {2 The combined Lwt+Option monad: for concurrent optional values} *)(** Syntax module for Lwt+Option. This is intended to be opened locally in
functions which use Lwt and [option] for control-flow. Within the scope of
this module, the code can include binding operators, leading to a
[let]-style syntax.
See also {!Lwt}, {!Option}. *)moduleLwt_option_syntax:sig(** [return x] is [Lwt.return (Some x)]. *)valreturn:'a->'aoptionLwt.t(** [return_unit] is [Lwt.return (Some ())] . *)valreturn_unit:unitoptionLwt.t(** [return_nil] is [Lwt.return (Some [])] . *)valreturn_nil:'alistoptionLwt.t(** [return_true] is [Lwt.return (Some true)] . *)valreturn_true:booloptionLwt.t(** [return_false] is [Lwt.return (Some false)] . *)valreturn_false:booloptionLwt.t(** Note that we do not provide [return_some] nor [return_none]. Both of
these functions are possible but somewhat confusing and rarely useful in
practice. If you need to carry [option]s within a LwtOption-monad
computation (yielding values of the type
['a option option Lwt.t]), you need to do so by hand:
[return (Some …)] and [return (None)]. *)(** [fail] is [Lwt.return None]. *)valfail:'aoptionLwt.t(** [let*] is a binding operator alias for {!Lwt.bind} mixed with {!Option.bind}. *)val(let*):'aoptionLwt.t->('a->'boptionLwt.t)->'boptionLwt.t(** [and*] is a binding operator alias for [both]. *)val(and*):'aoptionLwt.t->'boptionLwt.t->('a*'b)optionLwt.t(** [let+] is a binding operator alias for {!Lwt.map} mixed with {!Option.map}. *)val(let+):'aoptionLwt.t->('a->'b)->'boptionLwt.t(** [and*] is a binding operator alias for [both]. *)val(and+):'aoptionLwt.t->'boptionLwt.t->('a*'b)optionLwt.t(** The following values are for mixing expressions that are Lwt-only or
Option-only within the Lwt option monad. Note that there are fundamental
differences between [option] and [Lwt.t]: the former can be simply
matched on (i.e., it is possible to get out of the monad at any point)
whereas the latter can only be bound on (i.e., it is not possible to get
out of the monad). In addition, the former is for aborting computations
on failures (which in this case means that no value can be produced)
whereas the latter is for waiting before continuing.
Still, from a syntax point-of-view, both are handled the same way: with
a specialised binding operator. *)(** [let*!] is for binding Lwt-only expressions into the Lwt option combined
monad.
{[
let open Lwt_option_syntax in
let* x = … in
let*! y = … in
return (x + y)
]}
*)val(let*!):'aLwt.t->('a->'bLwt.t)->'bLwt.t(** [let*?] is for binding the value from Option-only
expressions into the Lwt option combined monad.
{[
let open Lwt_option_syntax in
let* x = … in
let*? y = … in
…
]} *)val(let*?):'aoption->('a->'boptionLwt.t)->'boptionLwt.t(** [both] is the joining of two concurrent optional non-unit values. *)valboth:'aoptionLwt.t->'boptionLwt.t->('a*'b)optionLwt.tend(** {2 The combined Lwt+Result monad: for concurrent successes/failures} *)(** Syntax module for Lwt+Result. This is intended to be opened locally in
functions which use Lwt and [result] for control-flow. Within the scope of
this module, the code can include binding operators, leading to a
[let]-style syntax.
See also {!Lwt}, {!Result}, and {!Lwt_result}. *)moduleLwt_result_syntax:sig(** [return x] is [Lwt.return (Ok x)] or [Lwt_result.return x]. *)valreturn:'a->('a,'e)resultLwt.t(** [return_unit] is [Lwt.return (Ok ())] . *)valreturn_unit:(unit,'e)resultLwt.t(** [return_none] is [Lwt.return (Ok None)] . *)valreturn_none:('aoption,'e)resultLwt.t(** [return_some x] is [Lwt.return (Ok (Some x))] . *)valreturn_some:'a->('aoption,'e)resultLwt.t(** [return_nil] is [Lwt.return (Ok [])] . *)valreturn_nil:('alist,'e)resultLwt.t(** [return_true] is [Lwt.return (Ok true)] . *)valreturn_true:(bool,'e)resultLwt.t(** [return_false] is [Lwt.return (Ok false)] . *)valreturn_false:(bool,'e)resultLwt.t(** Note that we do not provide [return_ok] nor [return_error]. Both of
these functions are possible but somewhat confusing and rarely useful in
practice. If you need to carry [result]s within a LwtResult-monad
computation (yielding values of the type
[(('a, 'e) result, 'e) result Lwt.t]), you need to do so by hand:
[return (Ok …)] and [return (Error …)]. *)(** [fail e] is [Lwt.return (Error e)]. *)valfail:'e->('a,'e)resultLwt.t(** [let*] is a binding operator alias for {!Lwt_result.bind}. *)val(let*):('a,'e)resultLwt.t->('a->('b,'e)resultLwt.t)->('b,'e)resultLwt.t(** [let+] is a binding operator alias for {!Lwt_result.map}. *)val(let+):('a,'e)resultLwt.t->('a->'b)->('b,'e)resultLwt.t(** Note that we do not provide [and*] nor [and+]. Both of these are
possible but their type is unsatisfying because the errors do not
compose well. You can use [both] (below) if need be. *)(** [lwt_map_error] is an Lwt-aware variant of {!Result.map_error}. It is
intended for mapping the errors of Lwt-result values. The main use of
this function is for mixing results that carry different types of
errors.
E.g., considering [fetch : unit -> (string, unit) result Lwt.t] and
[emit : string -> (unit, int) result Lwt.t], you can write
{[
let* data = lwt_map_error (fun () -> "fetching failed") @@ fetch () in
let* () =
lwt_map_error (fun code -> Format.asprintf "emit failed (%d)")
@@ emit data
in
..
]}
*)vallwt_map_error:('e->'f)->('a,'e)resultLwt.t->('a,'f)resultLwt.t(** The following values are for mixing expressions that are Lwt-only or
Result-only within the LwtResult monad. Note that there are fundamental
differences between [result] and [Lwt.t]: the former can be simply
matched on (i.e., it is possible to get out of the monad at any point)
whereas the latter can only be bound on (i.e., it is not possible to get
out of the monad). In addition, the former is for aborting computations
on failures whereas the latter is for waiting before continuing.
Still, from a syntax point-of-view, both are handled the same way: with
a specialised binding operator. *)(** [let*!] is for binding Lwt-only expressions into the LwtResult combined
monad.
{[
let open Lwt_result_syntax in
let* x = … in
let*! y = … in
return (x + y)
]}
*)val(let*!):'aLwt.t->('a->'bLwt.t)->'bLwt.t(** [let*?] is for binding the value from Result-only
expressions into the LwtResult combined monad.
{[
let open Lwt_result_syntax in
let* x = … in
let*? y = … in
…
]} *)val(let*?):('a,'e)result->('a->('b,'e)resultLwt.t)->('b,'e)resultLwt.t(** Note that you can mix [let*], [let*!], and [let*?] as needed, within a
single expression.
{[
let do_thing param =
let open Lwt_result_syntax in
let*? () = check_p param in (* Result-only for parameter checking *)
let*! () = log "starting doing the thing" in (* Lwt-only for infallible logging *)
let* r = thing param in
let*! () = log "done doing the thing" in (* Lwt-only for infallible logging *)
return r
]} *)(** [join] is the joining of concurrent success/failure unit values. *)valjoin:(unit,'e)resultLwt.tlist->(unit,'elist)resultLwt.t(** [all] is the joining of concurrent success/failure non-unit values. *)valall:('a,'e)resultLwt.tlist->('alist,'elist)resultLwt.t(** [both] is the joining of two concurrent success/failure non-unit values. *)valboth:('a,'e)resultLwt.t->('b,'e)resultLwt.t->('a*'b,'elist)resultLwt.tendend