package octez-proto-libs
List
A replacement for Stdlib.List
which:
- replaces the exception-raising functions by exception-safe variants,
- provides Lwt-, result- and Lwt-result-aware traversors.
List
is intended to shadow both Stdlib.List
and Lwt_list
.
Basics
Checkout Lwtreslib
for an introduction to the naming and semantic convention of Lwtreslib. In a nutshell:
- Stdlib functions that raise exceptions are replaced by safe variants (typically returning
option
). - The
_e
suffix is for result-aware traversors ("e" stands for "error"),_s
and_p
are for Lwt-aware, and_es
and_ep
are for Lwt-result-aware. _e
,_s
, and_es
traversors are fail-early: they stop traversal as soon as a failure (Error
orFail
) occurs;_p
and_ep
traversors are best-effort: they only resolve once all of the intermediate promises have, even if a failure occurs.
Double-traversal and combine
Note that double-list traversors (iter2
, map2
, etc., and also combine
) take an additional when_different_lengths
parameter. This is to control the error that is returned when the two lists passed as arguments have different lengths.
This mechanism is a replacement for Stdlib.List.iter2
(etc.) raising Invalid_argument
.
Note that, as per the fail-early behaviour mentioned above, _e
, _s
, and _es
traversors will have already processed the common-prefix before the error is returned.
Because the best-effort behaviour of _p
and _ep
is unsatisfying for this failure case, double parallel traversors are omitted from this library. (Specifically, it is not obvious whether nor how the when_different_lengths
error should be composed with the other errors.)
To obtain a different behaviour for sequential traversors, or to process two lists in parallel, you can use combine
or any of the alternatives that handles the error differently: combine_drop
, combine_with_leftovers
. Finally, the rev_combine
is provided to allow to avoid multiple-reversing.
Special considerations
Because they traverse the list from right-to-left, the fold_right2
function and all its variants fail with when_different_lengths
before any of the processing starts. Whilst this is still within the fail-early behaviour, it may be surprising enough that it requires mentioning here.
Because they may return early, for_all2
and exists2
and all their variants may return Ok _
even though the arguments have different lengths.
Trivial values
in-monad, preallocated nil
val nil_e : ('a list, 'trace) Pervasives.result
nil_e
is Ok []
val nil_s : 'a list Lwt.t
nil_s
is Lwt.return_nil
val nil_es : ('a list, 'trace) Pervasives.result Lwt.t
nil_es
is Lwt.return (Ok [])
Safe wrappers
Shadowing unsafe functions to avoid all exceptions.
Safe lookups, scans, retrievals
Return option rather than raise Not_found
, Failure _
, or Invalid_argument _
hd xs
is the head (first element) of the list or None
if the list is empty.
tl xs
is the tail of the list (the whole list except the first element) or None
if the list is empty.
nth xs n
is the n
th element of the list or None
if the list has fewer than n
elements.
nth xs 0 = hd xs
nth_opt
is an alias for nth
provided for backwards compatibility.
last x xs
is the last element of the list xs
or x
if xs
is empty.
The primary intended use for last
is after destructing a list: match l with | None -> … | Some x :: xs -> last x xs
but it can also be used for a default value: last default_value_if_empty xs
.
last_opt xs
is the last element of the list xs
or None
if the list xs
is empty.
find predicate xs
is the first element x
of the list xs
such that predicate x
is true
or None
if the list xs
has no such element.
find_opt
is an alias for find
provided for backwards compatibility.
mem ~equal a l
is true
iff there is an element e
of l
such that equal a e
.
assoc ~equal k kvs
is Some v
such that (k', v)
is the first pair in the list such that equal k' k
or None
if the list contains no such pair.
assoc_opt
is an alias for assoc
provided for backwards compatibility.
assq k kvs
is the same as assoc ~equal:Stdlib.( == ) k kvs
: it uses the physical equality.
assq_opt
is an alias for assq
provided for backwards compatibility.
mem_assoc ~equal k l
is equivalent to Option.is_some @@ assoc ~equal k l
.
remove_assoc ~equal k l
is l
without the first element (k', _)
such that equal k k'
.
remove_assoq k l
is remove_assoc ~equal:Stdlib.( == ) k l
.
Initialisation
val init :
when_negative_length:'trace ->
int ->
(int -> 'a) ->
('a list, 'trace) Pervasives.result
init ~when_negative_length n f
is Error when_negative_length
if n
is strictly negative and Ok (Stdlib.List.init n f)
otherwise.
Basic traversal
Double-list traversals
These safe-wrappers take an explicit value to handle the case of lists of unequal length.
val combine :
when_different_lengths:'trace ->
'a list ->
'b list ->
(('a * 'b) list, 'trace) Pervasives.result
combine ~when_different_lengths l1 l2
is either
Error when_different_lengths
ifList.length l1 <> List.length l2
- a list of pairs of elements from
l1
andl2
E.g., combine ~when_different_lengths [] [] = Ok []
E.g., combine ~when_different_lengths [1; 2] ['a'; 'b'] = Ok [(1,'a'); (2, 'b')]
E.g., combine ~when_different_lengths:() [1] [] = Error ()
Note: combine ~when_different_lengths l1 l2
is equivalent to try Ok (Stdlib.List.combine l1 l2)
with Invalid_argument _ -> when_different_lengths
The same equivalence almost holds for the other double traversors below. The notable difference is if the functions passed as argument to the traversors raise the Invalid_argument _
exception.
val rev_combine :
when_different_lengths:'trace ->
'a list ->
'b list ->
(('a * 'b) list, 'trace) Pervasives.result
rev_combine ~when_different_lengths xs ys
is rev (combine ~when_different_lengths xs ys)
but more efficient.
val iter2 :
when_different_lengths:'trace ->
('a -> 'b -> unit) ->
'a list ->
'b list ->
(unit, 'trace) Pervasives.result
val map2 :
when_different_lengths:'trace ->
('a -> 'b -> 'c) ->
'a list ->
'b list ->
('c list, 'trace) Pervasives.result
val rev_map2 :
when_different_lengths:'trace ->
('a -> 'b -> 'c) ->
'a list ->
'b list ->
('c list, 'trace) Pervasives.result
val fold_left2 :
when_different_lengths:'trace ->
('a -> 'b -> 'c -> 'a) ->
'a ->
'b list ->
'c list ->
('a, 'trace) Pervasives.result
val fold_right2 :
when_different_lengths:'trace ->
('a -> 'b -> 'c -> 'c) ->
'a list ->
'b list ->
'c ->
('c, 'trace) Pervasives.result
This function is not tail-recursive
fold_left_map f a xs
is a combination of fold_left
and map
that maps over all elements of xs
and threads an accumulator with initial value a
through calls to f
.
val for_all2 :
when_different_lengths:'trace ->
('a -> 'b -> bool) ->
'a list ->
'b list ->
(bool, 'trace) Pervasives.result
val exists2 :
when_different_lengths:'trace ->
('a -> 'b -> bool) ->
'a list ->
'b list ->
(bool, 'trace) Pervasives.result
Monad-aware variants
The functions below are strict extensions of the standard Stdlib.List
module. It is for result-, lwt- and lwt-result-aware variants. The meaning of the suffix is as described above, in Lwtreslib
, and in Sigs.Seq
.
Initialisation variants
Note that for asynchronous variants (_s
, _es
, _p
, and _ep
), if the length parameter is negative, then the promise is returned already fulfilled with Error when_different_lengths
.
val init_e :
when_negative_length:'trace ->
int ->
(int -> ('a, 'trace) Pervasives.result) ->
('a list, 'trace) Pervasives.result
val init_s :
when_negative_length:'trace ->
int ->
(int -> 'a Lwt.t) ->
('a list, 'trace) Pervasives.result Lwt.t
val init_es :
when_negative_length:'trace ->
int ->
(int -> ('a, 'trace) Pervasives.result Lwt.t) ->
('a list, 'trace) Pervasives.result Lwt.t
val init_p :
when_negative_length:'trace ->
int ->
(int -> 'a Lwt.t) ->
('a list, 'trace) Pervasives.result Lwt.t
Query variants
val find_e :
('a -> (bool, 'trace) Pervasives.result) ->
'a list ->
('a option, 'trace) Pervasives.result
val find_es :
('a -> (bool, 'trace) Pervasives.result Lwt.t) ->
'a list ->
('a option, 'trace) Pervasives.result Lwt.t
rev_filter f l
is rev (filter f l)
but more efficient.
val rev_filter_ok : ('a, 'b) Pervasives.result list -> 'a list
val filter_ok : ('a, 'b) Pervasives.result list -> 'a list
val rev_filter_error : ('a, 'b) Pervasives.result list -> 'b list
val filter_error : ('a, 'b) Pervasives.result list -> 'b list
val rev_filter_e :
('a -> (bool, 'trace) Pervasives.result) ->
'a list ->
('a list, 'trace) Pervasives.result
val filter_e :
('a -> (bool, 'trace) Pervasives.result) ->
'a list ->
('a list, 'trace) Pervasives.result
val rev_filter_es :
('a -> (bool, 'trace) Pervasives.result Lwt.t) ->
'a list ->
('a list, 'trace) Pervasives.result Lwt.t
val filter_es :
('a -> (bool, 'trace) Pervasives.result Lwt.t) ->
'a list ->
('a list, 'trace) Pervasives.result Lwt.t
val rev_partition_result : ('a, 'b) Pervasives.result list -> 'a list * 'b list
val partition_result : ('a, 'b) Pervasives.result list -> 'a list * 'b list
val rev_partition_e :
('a -> (bool, 'trace) Pervasives.result) ->
'a list ->
('a list * 'a list, 'trace) Pervasives.result
val partition_e :
('a -> (bool, 'trace) Pervasives.result) ->
'a list ->
('a list * 'a list, 'trace) Pervasives.result
val rev_partition_es :
('a -> (bool, 'trace) Pervasives.result Lwt.t) ->
'a list ->
('a list * 'a list, 'trace) Pervasives.result Lwt.t
val partition_es :
('a -> (bool, 'trace) Pervasives.result Lwt.t) ->
'a list ->
('a list * 'a list, 'trace) Pervasives.result Lwt.t
val iter_e :
('a -> (unit, 'trace) Pervasives.result) ->
'a list ->
(unit, 'trace) Pervasives.result
val iter_es :
('a -> (unit, 'trace) Pervasives.result Lwt.t) ->
'a list ->
(unit, 'trace) Pervasives.result Lwt.t
val iteri_e :
(int -> 'a -> (unit, 'trace) Pervasives.result) ->
'a list ->
(unit, 'trace) Pervasives.result
val iteri_es :
(int -> 'a -> (unit, 'trace) Pervasives.result Lwt.t) ->
'a list ->
(unit, 'trace) Pervasives.result Lwt.t
val map_e :
('a -> ('b, 'trace) Pervasives.result) ->
'a list ->
('b list, 'trace) Pervasives.result
val map_es :
('a -> ('b, 'trace) Pervasives.result Lwt.t) ->
'a list ->
('b list, 'trace) Pervasives.result Lwt.t
val mapi_e :
(int -> 'a -> ('b, 'trace) Pervasives.result) ->
'a list ->
('b list, 'trace) Pervasives.result
val mapi_es :
(int -> 'a -> ('b, 'trace) Pervasives.result Lwt.t) ->
'a list ->
('b list, 'trace) Pervasives.result Lwt.t
val rev_map_e :
('a -> ('b, 'trace) Pervasives.result) ->
'a list ->
('b list, 'trace) Pervasives.result
val rev_map_es :
('a -> ('b, 'trace) Pervasives.result Lwt.t) ->
'a list ->
('b list, 'trace) Pervasives.result Lwt.t
val rev_mapi_e :
(int -> 'a -> ('b, 'trace) Pervasives.result) ->
'a list ->
('b list, 'trace) Pervasives.result
val rev_mapi_es :
(int -> 'a -> ('b, 'trace) Pervasives.result Lwt.t) ->
'a list ->
('b list, 'trace) Pervasives.result Lwt.t
val rev_filter_map_e :
('a -> ('b option, 'trace) Pervasives.result) ->
'a list ->
('b list, 'trace) Pervasives.result
val filter_map_e :
('a -> ('b option, 'trace) Pervasives.result) ->
'a list ->
('b list, 'trace) Pervasives.result
val rev_filter_map_es :
('a -> ('b option, 'trace) Pervasives.result Lwt.t) ->
'a list ->
('b list, 'trace) Pervasives.result Lwt.t
val filter_map_es :
('a -> ('b option, 'trace) Pervasives.result Lwt.t) ->
'a list ->
('b list, 'trace) Pervasives.result Lwt.t
val concat_map_e :
('a -> ('b list, 'error) Pervasives.result) ->
'a list ->
('b list, 'error) Pervasives.result
val concat_map_es :
('a -> ('b list, 'error) Pervasives.result Lwt.t) ->
'a list ->
('b list, 'error) Pervasives.result Lwt.t
val fold_left_e :
('a -> 'b -> ('a, 'trace) Pervasives.result) ->
'a ->
'b list ->
('a, 'trace) Pervasives.result
val fold_left_es :
('a -> 'b -> ('a, 'trace) Pervasives.result Lwt.t) ->
'a ->
'b list ->
('a, 'trace) Pervasives.result Lwt.t
val fold_left_map_e :
('a -> 'b -> ('a * 'c, 'trace) Pervasives.result) ->
'a ->
'b list ->
('a * 'c list, 'trace) Pervasives.result
fold_left_map_e f a xs
is a combination of fold_left_e
and map_e
that maps over all elements of xs
and threads an accumulator with initial value a
through calls to f
. The list is traversed from left to right and the first encountered error is returned.
fold_left_map_s f a xs
is a combination of fold_left_s
and map_s
that maps over all elements of xs
and threads an accumulator with initial value a
through calls to f
.
val fold_left_map_es :
('a -> 'b -> ('a * 'c, 'trace) Pervasives.result Lwt.t) ->
'a ->
'b list ->
('a * 'c list, 'trace) Pervasives.result Lwt.t
fold_left_map_es f a xs
is a combination of fold_left_es
and map_es
that maps over all elements of xs
and threads an accumulator with initial value a
through calls to f
. The list is traversed from left to right and the first encountered error is returned.
val fold_left_i_e :
(int -> 'a -> 'b -> ('a, 'trace) Pervasives.result) ->
'a ->
'b list ->
('a, 'trace) Pervasives.result
val fold_left_i_es :
(int -> 'a -> 'b -> ('a, 'trace) Pervasives.result Lwt.t) ->
'a ->
'b list ->
('a, 'trace) Pervasives.result Lwt.t
val fold_right_e :
('a -> 'b -> ('b, 'trace) Pervasives.result) ->
'a list ->
'b ->
('b, 'trace) Pervasives.result
This function is not tail-recursive
This function is not tail-recursive
val fold_right_es :
('a -> 'b -> ('b, 'trace) Pervasives.result Lwt.t) ->
'a list ->
'b ->
('b, 'trace) Pervasives.result Lwt.t
This function is not tail-recursive
Double-traversal variants
As mentioned above, there are no _p
and _ep
double-traversors. Use combine
(and variants) to circumvent this.
val iter2_e :
when_different_lengths:'trace ->
('a -> 'b -> (unit, 'trace) Pervasives.result) ->
'a list ->
'b list ->
(unit, 'trace) Pervasives.result
val iter2_s :
when_different_lengths:'trace ->
('a -> 'b -> unit Lwt.t) ->
'a list ->
'b list ->
(unit, 'trace) Pervasives.result Lwt.t
val iter2_es :
when_different_lengths:'trace ->
('a -> 'b -> (unit, 'trace) Pervasives.result Lwt.t) ->
'a list ->
'b list ->
(unit, 'trace) Pervasives.result Lwt.t
val map2_e :
when_different_lengths:'trace ->
('a -> 'b -> ('c, 'trace) Pervasives.result) ->
'a list ->
'b list ->
('c list, 'trace) Pervasives.result
val map2_s :
when_different_lengths:'trace ->
('a -> 'b -> 'c Lwt.t) ->
'a list ->
'b list ->
('c list, 'trace) Pervasives.result Lwt.t
val map2_es :
when_different_lengths:'trace ->
('a -> 'b -> ('c, 'trace) Pervasives.result Lwt.t) ->
'a list ->
'b list ->
('c list, 'trace) Pervasives.result Lwt.t
val rev_map2_e :
when_different_lengths:'trace ->
('a -> 'b -> ('c, 'trace) Pervasives.result) ->
'a list ->
'b list ->
('c list, 'trace) Pervasives.result
val rev_map2_s :
when_different_lengths:'trace ->
('a -> 'b -> 'c Lwt.t) ->
'a list ->
'b list ->
('c list, 'trace) Pervasives.result Lwt.t
val rev_map2_es :
when_different_lengths:'trace ->
('a -> 'b -> ('c, 'trace) Pervasives.result Lwt.t) ->
'a list ->
'b list ->
('c list, 'trace) Pervasives.result Lwt.t
val fold_left2_e :
when_different_lengths:'trace ->
('a -> 'b -> 'c -> ('a, 'trace) Pervasives.result) ->
'a ->
'b list ->
'c list ->
('a, 'trace) Pervasives.result
val fold_left2_s :
when_different_lengths:'trace ->
('a -> 'b -> 'c -> 'a Lwt.t) ->
'a ->
'b list ->
'c list ->
('a, 'trace) Pervasives.result Lwt.t
val fold_left2_es :
when_different_lengths:'trace ->
('a -> 'b -> 'c -> ('a, 'trace) Pervasives.result Lwt.t) ->
'a ->
'b list ->
'c list ->
('a, 'trace) Pervasives.result Lwt.t
val fold_right2_e :
when_different_lengths:'trace ->
('a -> 'b -> 'c -> ('c, 'trace) Pervasives.result) ->
'a list ->
'b list ->
'c ->
('c, 'trace) Pervasives.result
This function is not tail-recursive
val fold_right2_s :
when_different_lengths:'trace ->
('a -> 'b -> 'c -> 'c Lwt.t) ->
'a list ->
'b list ->
'c ->
('c, 'trace) Pervasives.result Lwt.t
This function is not tail-recursive
val fold_right2_es :
when_different_lengths:'trace ->
('a -> 'b -> 'c -> ('c, 'trace) Pervasives.result Lwt.t) ->
'a list ->
'b list ->
'c ->
('c, 'trace) Pervasives.result Lwt.t
This function is not tail-recursive
Scanning variants
val for_all_e :
('a -> (bool, 'trace) Pervasives.result) ->
'a list ->
(bool, 'trace) Pervasives.result
val for_all_es :
('a -> (bool, 'trace) Pervasives.result Lwt.t) ->
'a list ->
(bool, 'trace) Pervasives.result Lwt.t
val exists_e :
('a -> (bool, 'trace) Pervasives.result) ->
'a list ->
(bool, 'trace) Pervasives.result
val exists_es :
('a -> (bool, 'trace) Pervasives.result Lwt.t) ->
'a list ->
(bool, 'trace) Pervasives.result Lwt.t
Double-scanning variants
As mentioned above, there are no _p
and _ep
double-scanners. Use combine
(and variants) to circumvent this.
val for_all2_e :
when_different_lengths:'trace ->
('a -> 'b -> (bool, 'trace) Pervasives.result) ->
'a list ->
'b list ->
(bool, 'trace) Pervasives.result
val for_all2_s :
when_different_lengths:'trace ->
('a -> 'b -> bool Lwt.t) ->
'a list ->
'b list ->
(bool, 'trace) Pervasives.result Lwt.t
val for_all2_es :
when_different_lengths:'trace ->
('a -> 'b -> (bool, 'trace) Pervasives.result Lwt.t) ->
'a list ->
'b list ->
(bool, 'trace) Pervasives.result Lwt.t
val exists2_e :
when_different_lengths:'trace ->
('a -> 'b -> (bool, 'trace) Pervasives.result) ->
'a list ->
'b list ->
(bool, 'trace) Pervasives.result
val exists2_s :
when_different_lengths:'trace ->
('a -> 'b -> bool Lwt.t) ->
'a list ->
'b list ->
(bool, 'trace) Pervasives.result Lwt.t
val exists2_es :
when_different_lengths:'trace ->
('a -> 'b -> (bool, 'trace) Pervasives.result Lwt.t) ->
'a list ->
'b list ->
(bool, 'trace) Pervasives.result Lwt.t
Combine variants
These are primarily intended to be used for preprocessing before applying a traversor to the resulting list of pairs. They give alternatives to the when_different_lengths
mechanism of the immediate double-traversors above.
In case the semantic of, say, map2_es
was unsatisfying, one can use map_es
on a combine
-preprocessed pair of lists. The different variants of combine
give different approaches to different-length handling.
combine_drop ll lr
is a list l
of pairs of elements taken from the common-length prefix of ll
and lr
. The suffix of whichever list is longer (if any) is dropped.
More formally nth l n
is:
None
ifn >= min (length ll) (length lr)
Some (Option.get @@ nth ll n, Option.get @@ nth lr n)
otherwise
A type like result
but which is symmetric
val combine_with_leftovers :
'a list ->
'b list ->
('a * 'b) list * ('a, 'b) left_or_right_list option
combine_with_leftovers ll lr
is a tuple (combined, leftover)
where combined
is combine_drop ll lr
and leftover
is either `Left lsuffix
or `Right rsuffix
depending on which of ll
or lr
is longer. leftover
is None
if the two lists have the same length.
compare / equal
Sorting
conversion
val to_seq : 'a list -> 'a Seq.t
val of_seq : 'a Seq.t -> 'a list
val init_ep :
when_negative_length:'error ->
int ->
(int -> ('a, 'error Error_monad.trace) Pervasives.result Lwt.t) ->
('a list, 'error Error_monad.trace) Pervasives.result Lwt.t
val filter_ep :
('a -> (bool, 'error Error_monad.trace) Pervasives.result Lwt.t) ->
'a list ->
('a list, 'error Error_monad.trace) Pervasives.result Lwt.t
val partition_ep :
('a -> (bool, 'error Error_monad.trace) Pervasives.result Lwt.t) ->
'a list ->
('a list * 'a list, 'error Error_monad.trace) Pervasives.result Lwt.t
val iter_ep :
('a -> (unit, 'error Error_monad.trace) Pervasives.result Lwt.t) ->
'a list ->
(unit, 'error Error_monad.trace) Pervasives.result Lwt.t
val iteri_ep :
(int -> 'a -> (unit, 'error Error_monad.trace) Pervasives.result Lwt.t) ->
'a list ->
(unit, 'error Error_monad.trace) Pervasives.result Lwt.t
val map_ep :
('a -> ('b, 'error Error_monad.trace) Pervasives.result Lwt.t) ->
'a list ->
('b list, 'error Error_monad.trace) Pervasives.result Lwt.t
val mapi_ep :
(int -> 'a -> ('b, 'error Error_monad.trace) Pervasives.result Lwt.t) ->
'a list ->
('b list, 'error Error_monad.trace) Pervasives.result Lwt.t
val rev_map_ep :
('a -> ('b, 'error Error_monad.trace) Pervasives.result Lwt.t) ->
'a list ->
('b list, 'error Error_monad.trace) Pervasives.result Lwt.t
val rev_mapi_ep :
(int -> 'a -> ('b, 'error Error_monad.trace) Pervasives.result Lwt.t) ->
'a list ->
('b list, 'error Error_monad.trace) Pervasives.result Lwt.t
val filter_map_ep :
('a -> ('b option, 'error Error_monad.trace) Pervasives.result Lwt.t) ->
'a list ->
('b list, 'error Error_monad.trace) Pervasives.result Lwt.t
val concat_map_ep :
('a -> ('b list, 'error Error_monad.trace) Pervasives.result Lwt.t) ->
'a list ->
('b list, 'error Error_monad.trace) Pervasives.result Lwt.t
val for_all_ep :
('a -> (bool, 'error Error_monad.trace) Pervasives.result Lwt.t) ->
'a list ->
(bool, 'error Error_monad.trace) Pervasives.result Lwt.t
val exists_ep :
('a -> (bool, 'error Error_monad.trace) Pervasives.result Lwt.t) ->
'a list ->
(bool, 'error Error_monad.trace) Pervasives.result Lwt.t