Make.ListA replacement for Stdlib.List which:
List is intended to shadow both Stdlib.List and Lwt_list.
Checkout Lwtreslib for an introduction to the naming and semantic convention of Lwtreslib. In a nutshell:
option)._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 or Fail) occurs; _p and _ep traversors are best-effort: they only resolve once all of the intermediate promises have, even if a failure occurs.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.
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.
in-monad, preallocated nil
val nil_e : ('a list, 'trace) Pervasives.resultnil_e is Ok []
val nil_s : 'a list Lwt.tnil_s is Lwt.return_nil
val nil_es : ('a list, 'trace) Pervasives.result Lwt.tnil_es is Lwt.return (Ok [])
Shadowing unsafe functions to avoid all exceptions.
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 nth 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.
val init :
when_negative_length:'trace ->
int ->
(int -> 'a) ->
('a list, 'trace) Pervasives.resultinit ~when_negative_length n f is Error when_negative_length if n is strictly negative and Ok (Stdlib.List.init n f) otherwise.
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.resultcombine ~when_different_lengths l1 l2 is either
Error when_different_lengths if List.length l1 <> List.length l2l1 and l2E.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.resultrev_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.resultval map2 :
when_different_lengths:'trace ->
('a -> 'b -> 'c) ->
'a list ->
'b list ->
('c list, 'trace) Pervasives.resultval rev_map2 :
when_different_lengths:'trace ->
('a -> 'b -> 'c) ->
'a list ->
'b list ->
('c list, 'trace) Pervasives.resultval fold_left2 :
when_different_lengths:'trace ->
('a -> 'b -> 'c -> 'a) ->
'a ->
'b list ->
'c list ->
('a, 'trace) Pervasives.resultval fold_right2 :
when_different_lengths:'trace ->
('a -> 'b -> 'c -> 'c) ->
'a list ->
'b list ->
'c ->
('c, 'trace) Pervasives.resultThis 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.resultval exists2 :
when_different_lengths:'trace ->
('a -> 'b -> bool) ->
'a list ->
'b list ->
(bool, 'trace) Pervasives.resultThe 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.
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.resultval init_s :
when_negative_length:'trace ->
int ->
(int -> 'a Lwt.t) ->
('a list, 'trace) Pervasives.result Lwt.tval init_es :
when_negative_length:'trace ->
int ->
(int -> ('a, 'trace) Pervasives.result Lwt.t) ->
('a list, 'trace) Pervasives.result Lwt.tval init_p :
when_negative_length:'trace ->
int ->
(int -> 'a Lwt.t) ->
('a list, 'trace) Pervasives.result Lwt.tval find_e :
('a -> (bool, 'trace) Pervasives.result) ->
'a list ->
('a option, 'trace) Pervasives.resultval find_es :
('a -> (bool, 'trace) Pervasives.result Lwt.t) ->
'a list ->
('a option, 'trace) Pervasives.result Lwt.trev_filter f l is rev (filter f l) but more efficient.
val rev_filter_ok : ('a, 'b) Pervasives.result list -> 'a listval filter_ok : ('a, 'b) Pervasives.result list -> 'a listval rev_filter_error : ('a, 'b) Pervasives.result list -> 'b listval filter_error : ('a, 'b) Pervasives.result list -> 'b listval rev_filter_e :
('a -> (bool, 'trace) Pervasives.result) ->
'a list ->
('a list, 'trace) Pervasives.resultval filter_e :
('a -> (bool, 'trace) Pervasives.result) ->
'a list ->
('a list, 'trace) Pervasives.resultval rev_filter_es :
('a -> (bool, 'trace) Pervasives.result Lwt.t) ->
'a list ->
('a list, 'trace) Pervasives.result Lwt.tval filter_es :
('a -> (bool, 'trace) Pervasives.result Lwt.t) ->
'a list ->
('a list, 'trace) Pervasives.result Lwt.tval rev_partition_result : ('a, 'b) Pervasives.result list -> 'a list * 'b listval partition_result : ('a, 'b) Pervasives.result list -> 'a list * 'b listval rev_partition_e :
('a -> (bool, 'trace) Pervasives.result) ->
'a list ->
('a list * 'a list, 'trace) Pervasives.resultval partition_e :
('a -> (bool, 'trace) Pervasives.result) ->
'a list ->
('a list * 'a list, 'trace) Pervasives.resultval rev_partition_es :
('a -> (bool, 'trace) Pervasives.result Lwt.t) ->
'a list ->
('a list * 'a list, 'trace) Pervasives.result Lwt.tval partition_es :
('a -> (bool, 'trace) Pervasives.result Lwt.t) ->
'a list ->
('a list * 'a list, 'trace) Pervasives.result Lwt.tval iter_e :
('a -> (unit, 'trace) Pervasives.result) ->
'a list ->
(unit, 'trace) Pervasives.resultval iter_es :
('a -> (unit, 'trace) Pervasives.result Lwt.t) ->
'a list ->
(unit, 'trace) Pervasives.result Lwt.tval iteri_e :
(int -> 'a -> (unit, 'trace) Pervasives.result) ->
'a list ->
(unit, 'trace) Pervasives.resultval iteri_es :
(int -> 'a -> (unit, 'trace) Pervasives.result Lwt.t) ->
'a list ->
(unit, 'trace) Pervasives.result Lwt.tval map_e :
('a -> ('b, 'trace) Pervasives.result) ->
'a list ->
('b list, 'trace) Pervasives.resultval map_es :
('a -> ('b, 'trace) Pervasives.result Lwt.t) ->
'a list ->
('b list, 'trace) Pervasives.result Lwt.tval mapi_e :
(int -> 'a -> ('b, 'trace) Pervasives.result) ->
'a list ->
('b list, 'trace) Pervasives.resultval mapi_es :
(int -> 'a -> ('b, 'trace) Pervasives.result Lwt.t) ->
'a list ->
('b list, 'trace) Pervasives.result Lwt.tval rev_map_e :
('a -> ('b, 'trace) Pervasives.result) ->
'a list ->
('b list, 'trace) Pervasives.resultval rev_map_es :
('a -> ('b, 'trace) Pervasives.result Lwt.t) ->
'a list ->
('b list, 'trace) Pervasives.result Lwt.tval rev_mapi_e :
(int -> 'a -> ('b, 'trace) Pervasives.result) ->
'a list ->
('b list, 'trace) Pervasives.resultval rev_mapi_es :
(int -> 'a -> ('b, 'trace) Pervasives.result Lwt.t) ->
'a list ->
('b list, 'trace) Pervasives.result Lwt.tval rev_filter_map_e :
('a -> ('b option, 'trace) Pervasives.result) ->
'a list ->
('b list, 'trace) Pervasives.resultval filter_map_e :
('a -> ('b option, 'trace) Pervasives.result) ->
'a list ->
('b list, 'trace) Pervasives.resultval rev_filter_map_es :
('a -> ('b option, 'trace) Pervasives.result Lwt.t) ->
'a list ->
('b list, 'trace) Pervasives.result Lwt.tval filter_map_es :
('a -> ('b option, 'trace) Pervasives.result Lwt.t) ->
'a list ->
('b list, 'trace) Pervasives.result Lwt.tval concat_map_e :
('a -> ('b list, 'error) Pervasives.result) ->
'a list ->
('b list, 'error) Pervasives.resultval concat_map_es :
('a -> ('b list, 'error) Pervasives.result Lwt.t) ->
'a list ->
('b list, 'error) Pervasives.result Lwt.tval fold_left_e :
('a -> 'b -> ('a, 'trace) Pervasives.result) ->
'a ->
'b list ->
('a, 'trace) Pervasives.resultval fold_left_es :
('a -> 'b -> ('a, 'trace) Pervasives.result Lwt.t) ->
'a ->
'b list ->
('a, 'trace) Pervasives.result Lwt.tval fold_left_map_e :
('a -> 'b -> ('a * 'c, 'trace) Pervasives.result) ->
'a ->
'b list ->
('a * 'c list, 'trace) Pervasives.resultfold_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.tfold_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.resultval fold_left_i_es :
(int -> 'a -> 'b -> ('a, 'trace) Pervasives.result Lwt.t) ->
'a ->
'b list ->
('a, 'trace) Pervasives.result Lwt.tval fold_right_e :
('a -> 'b -> ('b, 'trace) Pervasives.result) ->
'a list ->
'b ->
('b, 'trace) Pervasives.resultThis 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.tThis function is not tail-recursive
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.resultval iter2_s :
when_different_lengths:'trace ->
('a -> 'b -> unit Lwt.t) ->
'a list ->
'b list ->
(unit, 'trace) Pervasives.result Lwt.tval iter2_es :
when_different_lengths:'trace ->
('a -> 'b -> (unit, 'trace) Pervasives.result Lwt.t) ->
'a list ->
'b list ->
(unit, 'trace) Pervasives.result Lwt.tval map2_e :
when_different_lengths:'trace ->
('a -> 'b -> ('c, 'trace) Pervasives.result) ->
'a list ->
'b list ->
('c list, 'trace) Pervasives.resultval map2_s :
when_different_lengths:'trace ->
('a -> 'b -> 'c Lwt.t) ->
'a list ->
'b list ->
('c list, 'trace) Pervasives.result Lwt.tval map2_es :
when_different_lengths:'trace ->
('a -> 'b -> ('c, 'trace) Pervasives.result Lwt.t) ->
'a list ->
'b list ->
('c list, 'trace) Pervasives.result Lwt.tval rev_map2_e :
when_different_lengths:'trace ->
('a -> 'b -> ('c, 'trace) Pervasives.result) ->
'a list ->
'b list ->
('c list, 'trace) Pervasives.resultval rev_map2_s :
when_different_lengths:'trace ->
('a -> 'b -> 'c Lwt.t) ->
'a list ->
'b list ->
('c list, 'trace) Pervasives.result Lwt.tval 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.tval fold_left2_e :
when_different_lengths:'trace ->
('a -> 'b -> 'c -> ('a, 'trace) Pervasives.result) ->
'a ->
'b list ->
'c list ->
('a, 'trace) Pervasives.resultval fold_left2_s :
when_different_lengths:'trace ->
('a -> 'b -> 'c -> 'a Lwt.t) ->
'a ->
'b list ->
'c list ->
('a, 'trace) Pervasives.result Lwt.tval 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.tval fold_right2_e :
when_different_lengths:'trace ->
('a -> 'b -> 'c -> ('c, 'trace) Pervasives.result) ->
'a list ->
'b list ->
'c ->
('c, 'trace) Pervasives.resultThis 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.tThis 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.tThis function is not tail-recursive
val for_all_e :
('a -> (bool, 'trace) Pervasives.result) ->
'a list ->
(bool, 'trace) Pervasives.resultval for_all_es :
('a -> (bool, 'trace) Pervasives.result Lwt.t) ->
'a list ->
(bool, 'trace) Pervasives.result Lwt.tval exists_e :
('a -> (bool, 'trace) Pervasives.result) ->
'a list ->
(bool, 'trace) Pervasives.resultval exists_es :
('a -> (bool, 'trace) Pervasives.result Lwt.t) ->
'a list ->
(bool, 'trace) Pervasives.result Lwt.tAs 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.resultval for_all2_s :
when_different_lengths:'trace ->
('a -> 'b -> bool Lwt.t) ->
'a list ->
'b list ->
(bool, 'trace) Pervasives.result Lwt.tval for_all2_es :
when_different_lengths:'trace ->
('a -> 'b -> (bool, 'trace) Pervasives.result Lwt.t) ->
'a list ->
'b list ->
(bool, 'trace) Pervasives.result Lwt.tval exists2_e :
when_different_lengths:'trace ->
('a -> 'b -> (bool, 'trace) Pervasives.result) ->
'a list ->
'b list ->
(bool, 'trace) Pervasives.resultval exists2_s :
when_different_lengths:'trace ->
('a -> 'b -> bool Lwt.t) ->
'a list ->
'b list ->
(bool, 'trace) Pervasives.result Lwt.tval exists2_es :
when_different_lengths:'trace ->
('a -> 'b -> (bool, 'trace) Pervasives.result Lwt.t) ->
'a list ->
'b list ->
(bool, 'trace) Pervasives.result Lwt.tThese 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 if n >= min (length ll) (length lr)Some (Option.get @@ nth ll n, Option.get @@ nth lr n) otherwiseA type like result but which is symmetric
val combine_with_leftovers :
'a list ->
'b list ->
('a * 'b) list * ('a, 'b) left_or_right_list optioncombine_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.
val to_seq : 'a list -> 'a Seq.tval of_seq : 'a Seq.t -> 'a listval 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.tval filter_ep :
('a -> (bool, 'error Error_monad.trace) Pervasives.result Lwt.t) ->
'a list ->
('a list, 'error Error_monad.trace) Pervasives.result Lwt.tval 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.tval iter_ep :
('a -> (unit, 'error Error_monad.trace) Pervasives.result Lwt.t) ->
'a list ->
(unit, 'error Error_monad.trace) Pervasives.result Lwt.tval iteri_ep :
(int -> 'a -> (unit, 'error Error_monad.trace) Pervasives.result Lwt.t) ->
'a list ->
(unit, 'error Error_monad.trace) Pervasives.result Lwt.tval map_ep :
('a -> ('b, 'error Error_monad.trace) Pervasives.result Lwt.t) ->
'a list ->
('b list, 'error Error_monad.trace) Pervasives.result Lwt.tval mapi_ep :
(int -> 'a -> ('b, 'error Error_monad.trace) Pervasives.result Lwt.t) ->
'a list ->
('b list, 'error Error_monad.trace) Pervasives.result Lwt.tval rev_map_ep :
('a -> ('b, 'error Error_monad.trace) Pervasives.result Lwt.t) ->
'a list ->
('b list, 'error Error_monad.trace) Pervasives.result Lwt.tval 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.tval 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.tval 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.tval for_all_ep :
('a -> (bool, 'error Error_monad.trace) Pervasives.result Lwt.t) ->
'a list ->
(bool, 'error Error_monad.trace) Pervasives.result Lwt.tval exists_ep :
('a -> (bool, 'error Error_monad.trace) Pervasives.result Lwt.t) ->
'a list ->
(bool, 'error Error_monad.trace) Pervasives.result Lwt.t