Module CCList
Complements to list
type 'a gen= unit -> 'a optiontype 'a klist= unit -> [ `Nil | `Cons of 'a * 'a klist ]type 'a printer= Format.formatter -> 'a -> unittype 'a random_gen= Random.State.t -> 'a
include module type of List
val length : 'a list -> intval compare_lengths : 'a list -> 'b list -> intval compare_length_with : 'a list -> int -> intval cons : 'a -> 'a list -> 'a listval hd : 'a list -> 'aval tl : 'a list -> 'a listval nth : 'a list -> int -> 'aval nth_opt : 'a list -> int -> 'a optionval rev : 'a list -> 'a listval init : int -> (int -> 'a) -> 'a listval append : 'a list -> 'a list -> 'a listval rev_append : 'a list -> 'a list -> 'a listval concat : 'a list list -> 'a listval flatten : 'a list list -> 'a listval iter : ('a -> unit) -> 'a list -> unitval iteri : (int -> 'a -> unit) -> 'a list -> unitval map : ('a -> 'b) -> 'a list -> 'b listval mapi : (int -> 'a -> 'b) -> 'a list -> 'b listval rev_map : ('a -> 'b) -> 'a list -> 'b listval fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'aval fold_right : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'bval iter2 : ('a -> 'b -> unit) -> 'a list -> 'b list -> unitval map2 : ('a -> 'b -> 'c) -> 'a list -> 'b list -> 'c listval rev_map2 : ('a -> 'b -> 'c) -> 'a list -> 'b list -> 'c listval fold_left2 : ('a -> 'b -> 'c -> 'a) -> 'a -> 'b list -> 'c list -> 'aval fold_right2 : ('a -> 'b -> 'c -> 'c) -> 'a list -> 'b list -> 'c -> 'cval for_all : ('a -> bool) -> 'a list -> boolval exists : ('a -> bool) -> 'a list -> boolval for_all2 : ('a -> 'b -> bool) -> 'a list -> 'b list -> boolval exists2 : ('a -> 'b -> bool) -> 'a list -> 'b list -> boolval mem : 'a -> 'a list -> boolval memq : 'a -> 'a list -> boolval find : ('a -> bool) -> 'a list -> 'aval find_opt : ('a -> bool) -> 'a list -> 'a optionval filter : ('a -> bool) -> 'a list -> 'a listval find_all : ('a -> bool) -> 'a list -> 'a listval partition : ('a -> bool) -> 'a list -> 'a list * 'a listval assoc : 'a -> ('a * 'b) list -> 'bval assoc_opt : 'a -> ('a * 'b) list -> 'b optionval assq : 'a -> ('a * 'b) list -> 'bval assq_opt : 'a -> ('a * 'b) list -> 'b optionval mem_assoc : 'a -> ('a * 'b) list -> boolval mem_assq : 'a -> ('a * 'b) list -> boolval remove_assoc : 'a -> ('a * 'b) list -> ('a * 'b) listval remove_assq : 'a -> ('a * 'b) list -> ('a * 'b) listval split : ('a * 'b) list -> 'a list * 'b listval combine : 'a list -> 'b list -> ('a * 'b) listval sort : ('a -> 'a -> int) -> 'a list -> 'a listval stable_sort : ('a -> 'a -> int) -> 'a list -> 'a listval fast_sort : ('a -> 'a -> int) -> 'a list -> 'a listval sort_uniq : ('a -> 'a -> int) -> 'a list -> 'a listval merge : ('a -> 'a -> int) -> 'a list -> 'a list -> 'a list
val empty : 'a temptyis[].
val is_empty : _ t -> boolis_empty lreturnstrueiffl = [].- since
- 0.11
val cons_maybe : 'a option -> 'a t -> 'a tcons_maybe (Some x) lisx :: l.cons_maybe None lisl.- since
- 0.13
val filter : ('a -> bool) -> 'a t -> 'a tSafe version of
List.filter.filter p lreturns all the elements of the listlthat satisfy the predicatep. The order of the elements in the input list is preserved.
val fold_right : ('a -> 'b -> 'b) -> 'a t -> 'b -> 'bSafe version of
fold_right.fold_right f [a1; ...; an] bisf a1 (f a2 (... (f an b) ...)).
val fold_while : ('a -> 'b -> 'a * [ `Stop | `Continue ]) -> 'a -> 'b t -> 'aFold until a stop condition via
('a, `Stop)is indicated by the accumulator.- since
- 0.8
val fold_map : ('acc -> 'a -> 'acc * 'b) -> 'acc -> 'a list -> 'acc * 'b listfold_map f init lis afold_left-like function, but it also maps the list to another list.- since
- 0.14
val fold_map_i : ('acc -> int -> 'a -> 'acc * 'b) -> 'acc -> 'a list -> 'acc * 'b listfold_map_i f init lis afoldi-like function, but it also maps the list to another list.- since
- 2.8
val fold_on_map : f:('a -> 'b) -> reduce:('acc -> 'b -> 'acc) -> 'acc -> 'a list -> 'accfold_on_map ~f ~reduce init lcombinesmap fandfold_left reduce initin one operation.- since
- 2.8
val scan_left : ('acc -> 'a -> 'acc) -> 'acc -> 'a list -> 'acc listscan_left f init lreturns the list[init; f init x0; f (f init x0) x1; ...]wherex0,x1, etc. are the elements ofl.- since
- 1.2, but only
- since
- 2.2 with labels
val fold_map2 : ('acc -> 'a -> 'b -> 'acc * 'c) -> 'acc -> 'a list -> 'b list -> 'acc * 'c listfold_map2is tofold_mapwhatList.map2is toList.map.- raises Invalid_argument
if the lists do not have the same length.
- since
- 0.16
val fold_filter_map : ('acc -> 'a -> 'acc * 'b option) -> 'acc -> 'a list -> 'acc * 'b listfold_filter_map f init lis afold_left-like function, but also generates a list of output in a way similar tofilter_map.- since
- 0.17
val fold_filter_map_i : ('acc -> int -> 'a -> 'acc * 'b option) -> 'acc -> 'a list -> 'acc * 'b listfold_filter_map_i f init lis afoldi-like function, but also generates a list of output in a way similar tofilter_map.- since
- 2.8
val fold_flat_map : ('acc -> 'a -> 'acc * 'b list) -> 'acc -> 'a list -> 'acc * 'b listfold_flat_map f acc lis afold_left-like function, but it also maps the list to a list of lists that is thenflatten'd.- since
- 0.14
val fold_flat_map_i : ('acc -> int -> 'a -> 'acc * 'b list) -> 'acc -> 'a list -> 'acc * 'b listfold_flat_map_i f acc lis afold_left-like function, but it also maps the list to a list of lists that is thenflatten'd.- since
- 2.8
val count : ('a -> bool) -> 'a list -> intcount p lcounts how many elements oflsatisfy predicatep.- since
- 1.5, but only
- since
- 2.2 with labels
val init : int -> (int -> 'a) -> 'a tinit len fisf 0; f 1; ...; f (len-1).- raises Invalid_argument
if len < 0.
- since
- 0.6
val combine : 'a list -> 'b list -> ('a * 'b) listLike
List.combine but tail-recursive. Transform a pair of lists into a list of pairs:combine [a1; ...; an] [b1; ...; bn]is[(a1,b1); ...; (an,bn)].- raises Invalid_argument
if the lists have distinct lengths.
- since
- 1.2, but only
- since
- 2.2 with labels
val combine_gen : 'a list -> 'b list -> ('a * 'b) genLazy version of
combine. Unlikecombine, it does not fail if the lists have different lengths; instead, the output has as many pairs as the smallest input list.- since
- 1.2, but only
- since
- 2.2 with labels
val split : ('a * 'b) t -> 'a t * 'b tA tail-recursive version of
List.split. Transform a list of pairs into a pair of lists:split [(a1,b1); ...; (an,bn)]is([a1; ...; an], [b1; ...; bn]).- since
- 1.2, but only
- since
- 2.2 with labels
val compare : ('a -> 'a -> int) -> 'a t -> 'a t -> intval compare_lengths : 'a t -> 'b t -> intEquivalent to
compare (length l1) (length l2)but more efficient. Compare the lengths of two lists.- since
- 1.5, but only
- since
- 2.2 with labels
val compare_length_with : 'a t -> int -> intEquivalent to
compare (length l) xbut more efficient. Compare the length of a list to an integer.- since
- 1.5, but only
- since
- 2.2 with labels
val equal : ('a -> 'a -> bool) -> 'a t -> 'a t -> boolval flat_map : ('a -> 'b t) -> 'a t -> 'b tMap and flatten at the same time (safe). Evaluation order is not guaranteed.
val flat_map_i : (int -> 'a -> 'b t) -> 'a t -> 'b tMap with index and flatten at the same time (safe). Evaluation order is not guaranteed.
- since
- 2.8
val product : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c tCartesian product of the two lists, with the given combinator.
val cartesian_product : 'a t t -> 'a t tProduce the cartesian product of this list of lists, by returning all the ways of picking one element per sublist. NOTE the order of the returned list is unspecified. For example:
# cartesian_product [[1;2];[3];[4;5;6]] |> sort = [[1;3;4];[1;3;5];[1;3;6];[2;3;4];[2;3;5];[2;3;6]];; # cartesian_product [[1;2];[];[4;5;6]] = [];; # cartesian_product [[1;2];[3];[4];[5];[6]] |> sort = [[1;3;4;5;6];[2;3;4;5;6]];;invariant:
cartesian_product l = map_product id l.- since
- 1.2, but only
- since
- 2.2 with labels
val map_product_l : ('a -> 'b list) -> 'a list -> 'b list listmap_product_l f lmaps each element oflto a list of objects of type'busingf. We obtain[l1;l2;...;ln]wherelength l=nandli : 'b list. Then, it returns all the ways of picking exactly one element perli.- since
- 1.2, but only
- since
- 2.2 with labels
val diagonal : 'a t -> ('a * 'a) tAll pairs of distinct positions of the list.
list_diagonal lwill return the list ofList.nth i l, List.nth j lifi < j.
val partition_map : ('a -> [< `Left of 'b | `Right of 'c | `Drop ]) -> 'a list -> 'b list * 'c listpartition_map f lmapsfonland gather results in lists:- if
f x = `Left y, addsyto the first list. - if
f x = `Right z, addszto the second list. - if
f x = `Drop, ignoresx.
- since
- 0.11
- if
val group_by : ?hash:('a -> int) -> ?eq:('a -> 'a -> bool) -> 'a t -> 'a list tGroup equal elements, regardless of their order of appearance. precondition: for any
xandy, ifeq x ythenhash x=hash ymust hold.- since
- 2.3
val join : join_row:('a -> 'b -> 'c option) -> 'a t -> 'b t -> 'c tjoin ~join_row a bcombines every element ofawith every element ofbusingjoin_row. Ifjoin_rowreturns None, then the two elements do not combine. Assume thatballows for multiple iterations.- since
- 2.3
val join_by : ?eq:('key -> 'key -> bool) -> ?hash:('key -> int) -> ('a -> 'key) -> ('b -> 'key) -> merge:('key -> 'a -> 'b -> 'c option) -> 'a t -> 'b t -> 'c tjoin key1 key2 ~mergeis a binary operation that takes two sequencesaandb, projects their elements resp. withkey1andkey2, and combine values(x,y)from(a,b)with the samekeyusingmerge. IfmergereturnsNone, the combination of values is discarded. precondition: for anyxandy, ifeq x ythenhash x=hash ymust hold.- since
- 2.3
val join_all_by : ?eq:('key -> 'key -> bool) -> ?hash:('key -> int) -> ('a -> 'key) -> ('b -> 'key) -> merge:('key -> 'a list -> 'b list -> 'c option) -> 'a t -> 'b t -> 'c tjoin_all_by key1 key2 ~mergeis a binary operation that takes two sequencesaandb, projects their elements resp. withkey1andkey2, and, for each keykoccurring in at least one of them:- compute the list
l1of elements ofathat map tok - compute the list
l2of elements ofbthat map tok - call
merge k l1 l2. IfmergereturnsNone, the combination of values is discarded, otherwise it returnsSome candcis inserted in the result.
- since
- 2.3
- compute the list
val group_join_by : ?eq:('a -> 'a -> bool) -> ?hash:('a -> int) -> ('b -> 'a) -> 'a t -> 'b t -> ('a * 'b list) tgroup_join_by key2associates to every elementxof the first sequence, all the elementsyof the second sequence such thateq x (key y). Elements of the first sequences without corresponding values in the second one are mapped to[]precondition: for anyxandy, ifeq x ythenhash x=hash ymust hold.- since
- 2.3
val sublists_of_len : ?last:('a list -> 'a list option) -> ?offset:int -> int -> 'a list -> 'a list listsublists_of_len n lreturns sub-lists oflthat have lengthn. By default, these sub-lists are non overlapping:sublists_of_len 2 [1;2;3;4;5;6]returns[1;2]; [3;4]; [5;6].Examples:
sublists_of_len 2 [1;2;3;4;5;6] = [[1;2]; [3;4]; [5;6]].sublists_of_len 2 ~offset:3 [1;2;3;4;5;6] = [1;2];[4;5].sublists_of_len 3 ~last:CCOpt.return [1;2;3;4] = [1;2;3];[4].sublists_of_len 2 [1;2;3;4;5] = [[1;2]; [3;4]].
- parameter offset
the number of elements skipped between two consecutive sub-lists. By default it is
n. Ifoffset < n, the sub-lists will overlap; ifoffset > n, some elements will not appear at all.
- parameter last
if provided and the last group of elements
gis such thatlength g < n,last gis called. Iflast g = Some g',g'is appended; otherwisegis dropped. Iflast = CCOpt.return, it will simply keep the last group. By default,last = fun _ -> None, i.e. the last group is dropped if shorter thann.
- raises Invalid_argument
if
offset <= 0orn <= 0. SeeCCList.sublists_of_lenfor more details.
- since
- 1.0, but only
- since
- 1.5 with labels
val intersperse : 'a -> 'a list -> 'a listInsert the first argument between every element of the list.
- since
- 2.1, but only
- since
- 2.2 with labels
val interleave : 'a list -> 'a list -> 'a listinterleave [x1…xn] [y1…ym]isx1,y1,x2,y2,…and finishes with the suffix of the longest list.- since
- 2.1, but only
- since
- 2.2 with labels
val pure : 'a -> 'a tpureisreturn.
val return : 'a -> 'a treturn xisx.
val hd_tl : 'a t -> 'a * 'a thd_tl (x :: l)returnshd, l.- raises Failure
if the list is empty.
- since
- 0.16
val take_drop : int -> 'a t -> 'a t * 'a ttake_drop n lreturnsl1, l2such thatl1 @ l2 = landlength l1 = min (length l) n.
val take_while : ('a -> bool) -> 'a t -> 'a ttake_while f lreturns the longest prefix oflfor whichfistrue.- since
- 0.13
val drop_while : ('a -> bool) -> 'a t -> 'a tdrop_while f ldrops the longest prefix oflfor whichfistrue.- since
- 0.13
val take_drop_while : ('a -> bool) -> 'a t -> 'a t * 'a ttake_drop_while p l=take_while p l, drop_while p l.- since
- 1.2, but only
- since
- 2.2 with labels
val last : int -> 'a t -> 'a tlast n ltakes the lastnelements ofl(or less ifldoesn't have that many elements).
val head_opt : 'a t -> 'a optionFirst element.
- since
- 0.20
val last_opt : 'a t -> 'a optionLast element.
- since
- 0.20
val find_pred : ('a -> bool) -> 'a t -> 'a optionfind_pred p lfinds the first element oflthat satisfiesp, or returnsNoneif no element satisfiesp.- since
- 0.11
val find_opt : ('a -> bool) -> 'a t -> 'a optionSafe version of
find.- since
- 1.5, but only
- since
- 2.2 with labels
val find_pred_exn : ('a -> bool) -> 'a t -> 'aUnsafe version of
find_pred.- raises Not_found
if no such element is found.
- since
- 0.11
val find_map : ('a -> 'b option) -> 'a t -> 'b optionfind_map f ltraversesl, applyingfto each element. If for some elementx,f x = Some y, thenSome yis returned. Otherwise the call returnsNone.- since
- 0.11
val find_mapi : (int -> 'a -> 'b option) -> 'a t -> 'b optionLike
find_map, but also pass the index to the predicate function.- since
- 0.11
val find_idx : ('a -> bool) -> 'a t -> (int * 'a) optionfind_idx p xreturnsSome (i,x)wherexis thei-th element ofl, andp xholds. Otherwise returnsNone.
val remove : eq:('a -> 'a -> bool) -> key:'a -> 'a t -> 'a tremove ~key lremoves every instance ofkeyfroml. Tail-recursive.- parameter eq
equality function.
- since
- 0.11
val filter_map : ('a -> 'b option) -> 'a t -> 'b tfilter_map f lis the sublist oflcontaining only elements for whichfreturnsSome e. Map and remove elements at the same time.
val keep_some : 'a option t -> 'a tkeep_some lretains only elements of the formSome x. Likefilter_map CCFun.id.- since
- 1.3, but only
- since
- 2.2 with labels
val keep_ok : ('a, _) Pervasives.result t -> 'a tkeep_ok lretains only elements of the formOk x.- since
- 1.3, but only
- since
- 2.2 with labels
val all_some : 'a option t -> 'a t optionall_some lreturnsSome l'if all elements oflare of the formSome x, orNoneotherwise.- since
- 1.3, but only
- since
- 2.2 with labels
val all_ok : ('a, 'err) Pervasives.result t -> ('a t, 'err) Pervasives.resultall_ok lreturnsOk l'if all elements oflare of the formOk x, orError eotherwise (with the first error met).- since
- 1.3, but only
- since
- 2.2 with labels
val sorted_merge : cmp:('a -> 'a -> int) -> 'a list -> 'a list -> 'a listMerge elements from both sorted list.
val sort_uniq : cmp:('a -> 'a -> int) -> 'a list -> 'a listSort the list and remove duplicate elements.
val sorted_merge_uniq : cmp:('a -> 'a -> int) -> 'a list -> 'a list -> 'a listsorted_merge_uniq l1 l2merges the sorted listsl1andl2and removes duplicates.- since
- 0.10
val is_sorted : cmp:('a -> 'a -> int) -> 'a list -> boolis_sorted lreturnstrueifflis sorted (according to given order).- parameter cmp
the comparison function (default
Pervasives.compare).
- since
- 0.17
val sorted_insert : cmp:('a -> 'a -> int) -> ?uniq:bool -> 'a -> 'a list -> 'a listsorted_insert x linsertsxintolsuch that, iflwas sorted, thensorted_insert x lis sorted too.- parameter uniq
if true and
xis already in sorted position inl, thenxis not duplicated. Defaultfalse(xwill be inserted in any case).
- since
- 0.17
val uniq_succ : eq:('a -> 'a -> bool) -> 'a list -> 'a listuniq_succ lremoves duplicate elements that occur one next to the other. Examples:uniq_succ [1;2;1] = [1;2;1].uniq_succ [1;1;2] = [1;2].- since
- 0.10
val group_succ : eq:('a -> 'a -> bool) -> 'a list -> 'a list listgroup_succ ~eq lgroups together consecutive elements that are equal according toeq.- since
- 0.11
Indices
val mapi : (int -> 'a -> 'b) -> 'a t -> 'b tLike
map, but the function is applied to the index of the element as first argument (counting from 0), and the element itself as second argument.
val iteri : (int -> 'a -> unit) -> 'a t -> unitLike
iter, but the function is applied to the index of the element as first argument (counting from 0), and the element itself as second argument.
val iteri2 : (int -> 'a -> 'b -> unit) -> 'a t -> 'b t -> unitIter on two lists.
- raises Invalid_argument
when lists do not have the same length.
- since
- 2.0, but only
- since
- 2.2 with labels
val foldi : ('b -> int -> 'a -> 'b) -> 'b -> 'a t -> 'bLike
foldbut it also passes in the index of each element to the folded function. Tail-recursive.
val foldi2 : ('c -> int -> 'a -> 'b -> 'c) -> 'c -> 'a t -> 'b t -> 'cFold on two lists, with index.
- raises Invalid_argument
when lists do not have the same length.
- since
- 2.0, but only
- since
- 2.2 with labels
val get_at_idx : int -> 'a t -> 'a optionGet by index in the list. If the index is negative, it will get element starting from the end of the list.
val nth_opt : 'a t -> int -> 'a optionSafe version of
nth.- raises Invalid_argument
if the int is negative.
- since
- 1.5, but only
- since
- 2.2 with labels
val get_at_idx_exn : int -> 'a t -> 'aGet the i-th element, or
- raises Not_found
if the index is invalid. If the index is negative, it will get element starting from the end of the list.
val set_at_idx : int -> 'a -> 'a t -> 'a tSet i-th element (removes the old one), or does nothing if index is too high. If the index is negative, it will set element starting from the end of the list.
Set Operators
Those operations maintain the invariant that the list does not contain duplicates (if it already satisfies it).
val add_nodup : eq:('a -> 'a -> bool) -> 'a -> 'a t -> 'a tadd_nodup x setaddsxtosetif it was not already present. Linear time.- since
- 0.11
val remove_one : eq:('a -> 'a -> bool) -> 'a -> 'a t -> 'a tremove_one x setremoves one occurrence ofxfromset. Linear time.- since
- 0.11
val mem : eq:('a -> 'a -> bool) -> 'a -> 'a t -> boolMembership to the list. Linear time.
val uniq : eq:('a -> 'a -> bool) -> 'a t -> 'a tRemove duplicates w.r.t the equality predicate. Complexity is quadratic in the length of the list, but the order of elements is preserved. If you wish for a faster de-duplication but do not care about the order, use
sort_uniq.
Other Constructors
val range_by : step:int -> int -> int -> int trange_by ~step i jiterates on integers fromitojincluded, where the difference between successive elements isstep. Use a negativestepfor a decreasing list.- raises Invalid_argument
if
step=0.
- since
- 0.18
val range : int -> int -> int trange i jiterates on integers fromitojincluded. It works both for decreasing and increasing ranges.
val range' : int -> int -> int tLike
rangebut the second bound is excluded. For instancerange' 0 5 = [0;1;2;3;4].
val (--) : int -> int -> int tInfix alias for
range.
val (--^) : int -> int -> int tInfix alias for
range'.- since
- 0.17
val replicate : int -> 'a -> 'a tReplicate the given element
ntimes.
Association Lists
module Assoc : sig ... endval assoc : eq:('a -> 'a -> bool) -> 'a -> ('a * 'b) t -> 'bLike
Assoc.get_exn.- since
- 2.0
val assoc_opt : eq:('a -> 'a -> bool) -> 'a -> ('a * 'b) t -> 'b optionLike
Assoc.get.- since
- 1.5, but only
- since
- 2.0 with labels
val assq_opt : 'a -> ('a * 'b) t -> 'b optionSafe version of
assq.- since
- 1.5, but only
- since
- 2.0 with labels
val mem_assoc : eq:('a -> 'a -> bool) -> 'a -> ('a * _) t -> boolLike
Assoc.mem.- since
- 2.0
References on Lists
- since
- 0.3.3
module Ref : sig ... endmodule type MONAD = sig ... end
Conversions
val random : 'a random_gen -> 'a t random_genval random_non_empty : 'a random_gen -> 'a t random_genval random_len : int -> 'a random_gen -> 'a t random_genval random_choose : 'a t -> 'a random_genRandomly choose an element in the list.
- raises Not_found
if the list is empty.
val random_sequence : 'a random_gen t -> 'a t random_genval to_string : ?start:string -> ?stop:string -> ?sep:string -> ('a -> string) -> 'a t -> stringto_string ~start ~stop ~sep item_to_string lprintlto a string usingsepas a separator between elements ofl.- since
- 2.7
val to_std_seq : 'a t -> 'a Seq.tReturn a
Seq.tof the elements of the list.- since
- 2.8
val to_seq : 'a t -> 'a sequenceval of_iter : 'a iter -> 'a tBuild a list from a given
iter. In the result, elements appear in the same order as they did in the sourceiter.- since
- 2.8
val of_std_seq_rev : 'a Seq.t -> 'a tBuild a list from a given
Seq.t, in reverse order.- since
- 2.8
val of_std_seq : 'a Seq.t -> 'a tBuild a list from a given
Seq.t. In the result, elements appear in the same order as they did in the sourceseq.- since
- 2.8
val of_seq : 'a sequence -> 'a tval to_gen : 'a t -> 'a genReturn a
genof the elements of the list.
Infix Operators
It is convenient to open CCList.Infix to access the infix operators without cluttering the scope too much.
- since
- 0.16
module Infix : sig ... endLet operators on OCaml >= 4.08.0, nothing otherwise
- since
- 2.8