123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776open!ImportmoduleKey=structmoduletypeS=sigtypet[@@deriving_inlinecompare,sexp_of]includesig[@@@ocaml.warning"-32"]valcompare:t->t->intvalsexp_of_t:t->Ppx_sexp_conv_lib.Sexp.tend[@@ocaml.doc"@inline"][@@@end](** Two [t]s that [compare] equal must have equal hashes for the hashtable
to behave properly. *)valhash:t->intendtype'at=(moduleSwithtypet='a)endmoduletypeAccessors=sig(** {2 Accessors} *)type('a,'b)ttype'akeyvalsexp_of_key:('a,_)t->'akey->Sexp.tvalclear:(_,_)t->unitvalcopy:('a,'b)t->('a,'b)t(** Attempting to modify ([set], [remove], etc.) the hashtable during iteration ([fold],
[iter], [iter_keys], [iteri]) will raise an exception. *)valfold:('a,'b)t->init:'c->f:(key:'akey->data:'b->'c->'c)->'cvaliter_keys:('a,_)t->f:('akey->unit)->unitvaliter:(_,'b)t->f:('b->unit)->unit(** Iterates over both keys and values.
Example:
{v
let h = Hashtbl.of_alist_exn (module Int) [(1, 4); (5, 6)] in
Hashtbl.iteri h ~f:(fun ~key ~data ->
print_endline (Printf.sprintf "%d-%d" key data));;
1-4
5-6
- : unit = ()
v} *)valiteri:('a,'b)t->f:(key:'akey->data:'b->unit)->unitvalexistsi:('a,'b)t->f:(key:'akey->data:'b->bool)->boolvalexists:(_,'b)t->f:('b->bool)->boolvalfor_alli:('a,'b)t->f:(key:'akey->data:'b->bool)->boolvalfor_all:(_,'b)t->f:('b->bool)->boolvalcounti:('a,'b)t->f:(key:'akey->data:'b->bool)->intvalcount:(_,'b)t->f:('b->bool)->intvallength:(_,_)t->intvalis_empty:(_,_)t->boolvalmem:('a,_)t->'akey->boolvalremove:('a,_)t->'akey->unitvalchoose:('a,'b)t->('akey*'b)optionvalchoose_exn:('a,'b)t->'akey*'b(** Sets the given [key] to [data]. *)valset:('a,'b)t->key:'akey->data:'b->unit(** [add] and [add_exn] leave the table unchanged if the key was already present. *)valadd:('a,'b)t->key:'akey->data:'b->[`Ok|`Duplicate]valadd_exn:('a,'b)t->key:'akey->data:'b->unit(** [change t key ~f] changes [t]'s value for [key] to be [f (find t key)]. *)valchange:('a,'b)t->'akey->f:('boption->'boption)->unit(** [update t key ~f] is [change t key ~f:(fun o -> Some (f o))]. *)valupdate:('a,'b)t->'akey->f:('boption->'b)->unit(** [map t f] returns a new table with values replaced by the result of applying [f]
to the current values.
Example:
{v
let h = Hashtbl.of_alist_exn (module Int) [(1, 4); (5, 6)] in
let h' = Hashtbl.map h ~f:(fun x -> x * 2) in
Hashtbl.to_alist h';;
- : (int * int) list = [(5, 12); (1, 8)]
v} *)valmap:('a,'b)t->f:('b->'c)->('a,'c)t(** Like [map], but the function [f] takes both key and data as arguments. *)valmapi:('a,'b)t->f:(key:'akey->data:'b->'c)->('a,'c)t(** Returns a new table by filtering the given table's values by [f]: the keys for which
[f] applied to the current value returns [Some] are kept, and those for which it
returns [None] are discarded.
Example:
{v
let h = Hashtbl.of_alist_exn (module Int) [(1, 4); (5, 6)] in
Hashtbl.filter_map h ~f:(fun x -> if x > 5 then Some x else None)
|> Hashtbl.to_alist;;
- : (int * int) list = [(5, 6)]
v} *)valfilter_map:('a,'b)t->f:('b->'coption)->('a,'c)t(** Like [filter_map], but the function [f] takes both key and data as arguments. *)valfilter_mapi:('a,'b)t->f:(key:'akey->data:'b->'coption)->('a,'c)tvalfilter_keys:('a,'b)t->f:('akey->bool)->('a,'b)tvalfilter:('a,'b)t->f:('b->bool)->('a,'b)tvalfilteri:('a,'b)t->f:(key:'akey->data:'b->bool)->('a,'b)t(** Returns new tables with bound values partitioned by [f] applied to the bound
values. *)valpartition_map:('a,'b)t->f:('b->[`Fstof'c|`Sndof'd])->('a,'c)t*('a,'d)t(** Like [partition_map], but the function [f] takes both key and data as arguments. *)valpartition_mapi:('a,'b)t->f:(key:'akey->data:'b->[`Fstof'c|`Sndof'd])->('a,'c)t*('a,'d)t(** Returns a pair of tables [(t1, t2)], where [t1] contains all the elements of the
initial table which satisfy the predicate [f], and [t2] contains the rest. *)valpartition_tf:('a,'b)t->f:('b->bool)->('a,'b)t*('a,'b)t(** Like [partition_tf], but the function [f] takes both key and data as arguments. *)valpartitioni_tf:('a,'b)t->f:(key:'akey->data:'b->bool)->('a,'b)t*('a,'b)t(** [find_or_add t k ~default] returns the data associated with key [k] if it is in the
table [t], and otherwise assigns [k] the value returned by [default ()]. *)valfind_or_add:('a,'b)t->'akey->default:(unit->'b)->'b(** Like [find_or_add] but [default] takes the key as an argument. *)valfindi_or_add:('a,'b)t->'akey->default:('akey->'b)->'b(** [find t k] returns [Some] (the current binding) of [k] in [t], or [None] if no such
binding exists. *)valfind:('a,'b)t->'akey->'boption(** [find_exn t k] returns the current binding of [k] in [t], or raises [Caml.Not_found]
or [Not_found_s] if no such binding exists. *)valfind_exn:('a,'b)t->'akey->'b(** [find_and_call t k ~if_found ~if_not_found]
is equivalent to:
[match find t k with Some v -> if_found v | None -> if_not_found k]
except that it doesn't allocate the option. *)valfind_and_call:('a,'b)t->'akey->if_found:('b->'c)->if_not_found:('akey->'c)->'cvalfindi_and_call:('a,'b)t->'akey->if_found:(key:'akey->data:'b->'c)->if_not_found:('akey->'c)->'c(** [find_and_remove t k] returns Some (the current binding) of k in t and removes it,
or None is no such binding exists. *)valfind_and_remove:('a,'b)t->'akey->'boption(** Merges two hashtables.
The result of [merge f h1 h2] has as keys the set of all [k] in the union of the
sets of keys of [h1] and [h2] for which [d(k)] is not None, where:
d(k) =
- [f ~key:k (`Left d1)]
if [k] in [h1] maps to d1, and [h2] does not have data for [k];
- [f ~key:k (`Right d2)]
if [k] in [h2] maps to d2, and [h1] does not have data for [k];
- [f ~key:k (`Both (d1, d2))]
otherwise, where [k] in [h1] maps to [d1] and [k] in [h2] maps to [d2].
Each key [k] is mapped to a single piece of data [x], where [d(k) = Some x].
Example:
{v
let h1 = Hashtbl.of_alist_exn (module Int) [(1, 5); (2, 3232)] in
let h2 = Hashtbl.of_alist_exn (module Int) [(1, 3)] in
Hashtbl.merge h1 h2 ~f:(fun ~key:_ -> function
| `Left x -> Some (`Left x)
| `Right x -> Some (`Right x)
| `Both (x, y) -> if x=y then None else Some (`Both (x,y))
) |> Hashtbl.to_alist;;
- : (int * [> `Both of int * int | `Left of int | `Right of int ]) list =
[(2, `Left 3232); (1, `Both (5, 3))]
v} *)valmerge:('k,'a)t->('k,'b)t->f:(key:'kkey->[`Leftof'a|`Rightof'b|`Bothof'a*'b]->'coption)->('k,'c)t(** Every [key] in [src] will be removed or set in [dst] according to the return value
of [f]. *)type'amerge_into_action=|Remove|Set_toof'avalmerge_into:src:('k,'a)t->dst:('k,'b)t->f:(key:'kkey->'a->'boption->'bmerge_into_action)->unit(** Returns the list of all keys for given hashtable. *)valkeys:('a,_)t->'akeylist(** Returns the list of all data for given hashtable. *)valdata:(_,'b)t->'blist(** [filter_inplace t ~f] removes all the elements from [t] that don't satisfy [f]. *)valfilter_keys_inplace:('a,_)t->f:('akey->bool)->unitvalfilter_inplace:(_,'b)t->f:('b->bool)->unitvalfilteri_inplace:('a,'b)t->f:(key:'akey->data:'b->bool)->unit(** [map_inplace t ~f] applies [f] to all elements in [t], transforming them in
place. *)valmap_inplace:(_,'b)t->f:('b->'b)->unitvalmapi_inplace:('a,'b)t->f:(key:'akey->data:'b->'b)->unit(** [filter_map_inplace] combines the effects of [map_inplace] and [filter_inplace]. *)valfilter_map_inplace:(_,'b)t->f:('b->'boption)->unitvalfilter_mapi_inplace:('a,'b)t->f:(key:'akey->data:'b->'boption)->unit(** [equal t1 t2 f] and [similar t1 t2 f] both return true iff [t1] and [t2] have the
same keys and for all keys [k], [f (find_exn t1 k) (find_exn t2 k)]. [equal] and
[similar] only differ in their types. *)valequal:('a,'b)t->('a,'b)t->('b->'b->bool)->boolvalsimilar:('a,'b1)t->('a,'b2)t->('b1->'b2->bool)->bool(** Returns the list of all (key, data) pairs for given hashtable. *)valto_alist:('a,'b)t->('akey*'b)listvalvalidate:name:('akey->string)->'bValidate.check->('a,'b)tValidate.check(** [remove_if_zero]'s default is [false]. *)valincr:?by:int->?remove_if_zero:bool->('a,int)t->'akey->unitvaldecr:?by:int->?remove_if_zero:bool->('a,int)t->'akey->unitendmoduletypeMulti=sigtype('a,'b)ttype'akey(** [add_multi t ~key ~data] if [key] is present in the table then cons
[data] on the list, otherwise add [key] with a single element list. *)valadd_multi:('a,'blist)t->key:'akey->data:'b->unit(** [remove_multi t key] updates the table, removing the head of the list bound to
[key]. If the list has only one element (or is empty) then the binding is
removed. *)valremove_multi:('a,_list)t->'akey->unit(** [find_multi t key] returns the empty list if [key] is not present in the table,
returns [t]'s values for [key] otherwise. *)valfind_multi:('a,'blist)t->'akey->'blistendtype('key,'data,'z)create_options=?growth_allowed:bool(** defaults to [true] *)->?size:int(** initial size -- default 0 *)->'keyKey.t->'ztype('key,'data,'z)create_options_without_first_class_module=?growth_allowed:bool(** defaults to [true] *)->?size:int(** initial size -- default 0 *)->'zmoduletypeCreators_generic=sigtype('a,'b)ttype'akeytype('key,'data,'z)create_optionsvalcreate:('akey,'b,unit->('a,'b)t)create_optionsvalof_alist:('akey,'b,('akey*'b)list->[`Okof('a,'b)t|`Duplicate_keyof'akey])create_optionsvalof_alist_report_all_dups:('akey,'b,('akey*'b)list->[`Okof('a,'b)t|`Duplicate_keysof'akeylist])create_optionsvalof_alist_or_error:('akey,'b,('akey*'b)list->('a,'b)tOr_error.t)create_optionsvalof_alist_exn:('akey,'b,('akey*'b)list->('a,'b)t)create_optionsvalof_alist_multi:('akey,'blist,('akey*'b)list->('a,'blist)t)create_options(** {[ create_mapped get_key get_data [x1,...,xn]
= of_alist [get_key x1, get_data x1; ...; get_key xn, get_data xn] ]} *)valcreate_mapped:('akey,'b,get_key:('r->'akey)->get_data:('r->'b)->'rlist->[`Okof('a,'b)t|`Duplicate_keysof'akeylist])create_options(** {[ create_with_key ~get_key [x1,...,xn]
= of_alist [get_key x1, x1; ...; get_key xn, xn] ]} *)valcreate_with_key:('akey,'r,get_key:('r->'akey)->'rlist->[`Okof('a,'r)t|`Duplicate_keysof'akeylist])create_optionsvalcreate_with_key_or_error:('akey,'r,get_key:('r->'akey)->'rlist->('a,'r)tOr_error.t)create_optionsvalcreate_with_key_exn:('akey,'r,get_key:('r->'akey)->'rlist->('a,'r)t)create_optionsvalgroup:('akey,'b,get_key:('r->'akey)->get_data:('r->'b)->combine:('b->'b->'b)->'rlist->('a,'b)t)create_optionsendmoduletypeCreators=sigtype('a,'b)t(** {2 Creators} *)(** The module you pass to [create] must have a type that is hashable, sexpable, and
comparable.
Example:
{v
Hashtbl.create (module Int);;
- : (int, '_a) Hashtbl.t = <abstr>;;
v} *)valcreate:?growth_allowed:bool(** defaults to [true] *)->?size:int(** initial size -- default 0 *)->'aKey.t->('a,'b)t(** Example:
{v
Hashtbl.of_alist (module Int) [(3, "something"); (2, "whatever")]
- : [ `Duplicate_key of int | `Ok of (int, string) Hashtbl.t ] = `Ok <abstr>
v} *)valof_alist:?growth_allowed:bool(** defaults to [true] *)->?size:int(** initial size -- default 0 *)->'aKey.t->('a*'b)list->[`Okof('a,'b)t|`Duplicate_keyof'a](** Whereas [of_alist] will report [Duplicate_key] no matter how many dups there are in
your list, [of_alist_report_all_dups] will report each and every duplicate entry.
For example:
{v
Hashtbl.of_alist (module Int) [(1, "foo"); (1, "bar"); (2, "foo"); (2, "bar")];;
- : [ `Duplicate_key of int | `Ok of (int, string) Hashtbl.t ] = `Duplicate_key 1
Hashtbl.of_alist_report_all_dups (module Int) [(1, "foo"); (1, "bar"); (2, "foo"); (2, "bar")];;
- : [ `Duplicate_keys of int list | `Ok of (int, string) Hashtbl.t ] = `Duplicate_keys [1; 2]
v} *)valof_alist_report_all_dups:?growth_allowed:bool(** defaults to [true] *)->?size:int(** initial size -- default 0 *)->'aKey.t->('a*'b)list->[`Okof('a,'b)t|`Duplicate_keysof'alist]valof_alist_or_error:?growth_allowed:bool(** defaults to [true] *)->?size:int(** initial size -- default 0 *)->'aKey.t->('a*'b)list->('a,'b)tOr_error.tvalof_alist_exn:?growth_allowed:bool(** defaults to [true] *)->?size:int(** initial size -- default 0 *)->'aKey.t->('a*'b)list->('a,'b)t(** Creates a {{!Multi} "multi"} hashtable, i.e., a hashtable where each key points to a
list potentially containing multiple values. So instead of short-circuiting with a
[`Duplicate_key] variant on duplicates, as in [of_alist], [of_alist_multi] folds
those values into a list for the given key:
{v
let h = Hashtbl.of_alist_multi (module Int) [(1, "a"); (1, "b"); (2, "c"); (2, "d")];;
val h : (int, string list) Hashtbl.t = <abstr>
Hashtbl.find_exn h 1;;
- : string list = ["b"; "a"]
v} *)valof_alist_multi:?growth_allowed:bool(** defaults to [true] *)->?size:int(** initial size -- default 0 *)->'aKey.t->('a*'b)list->('a,'blist)t(** Applies the [get_key] and [get_data] functions to the ['r list] to create the
initial keys and values, respectively, for the new hashtable.
{[ create_mapped get_key get_data [x1;...;xn]
= of_alist [get_key x1, get_data x1; ...; get_key xn, get_data xn]
]}
Example:
{v
let h =
Hashtbl.create_mapped (module Int)
~get_key:(fun x -> x)
~get_data:(fun x -> x + 1)
[1; 2; 3];;
val h : [ `Duplicate_keys of int list | `Ok of (int, int) Hashtbl.t ] = `Ok <abstr>
let h =
match h with
| `Ok x -> x
| `Duplicate_keys _ -> failwith ""
in
Hashtbl.find_exn h 1;;
- : int = 2
v} *)valcreate_mapped:?growth_allowed:bool(** defaults to [true] *)->?size:int(** initial size -- default 0 *)->'aKey.t->get_key:('r->'a)->get_data:('r->'b)->'rlist->[`Okof('a,'b)t|`Duplicate_keysof'alist](** {[ create_with_key ~get_key [x1;...;xn]
= of_alist [get_key x1, x1; ...; get_key xn, xn] ]} *)valcreate_with_key:?growth_allowed:bool(** defaults to [true] *)->?size:int(** initial size -- default 0 *)->'aKey.t->get_key:('r->'a)->'rlist->[`Okof('a,'r)t|`Duplicate_keysof'alist]valcreate_with_key_or_error:?growth_allowed:bool(** defaults to [true] *)->?size:int(** initial size -- default 0 *)->'aKey.t->get_key:('r->'a)->'rlist->('a,'r)tOr_error.tvalcreate_with_key_exn:?growth_allowed:bool(** defaults to [true] *)->?size:int(** initial size -- default 0 *)->'aKey.t->get_key:('r->'a)->'rlist->('a,'r)t(** Like [create_mapped], applies the [get_key] and [get_data] functions to the ['r
list] to create the initial keys and values, respectively, for the new hashtable --
and then, like [add_multi], folds together values belonging to the same keys. Here,
though, the function used for the folding is given by [combine] (instead of just
being a [cons]).
Example:
{v
Hashtbl.group (module Int)
~get_key:(fun x -> x / 2)
~get_data:(fun x -> x)
~combine:(fun x y -> x * y)
[ 1; 2; 3; 4]
|> Hashtbl.to_alist;;
- : (int * int) list = [(2, 4); (1, 6); (0, 1)]
v} *)valgroup:?growth_allowed:bool(** defaults to [true] *)->?size:int(** initial size -- default 0 *)->'aKey.t->get_key:('r->'a)->get_data:('r->'b)->combine:('b->'b->'b)->'rlist->('a,'b)tendmoduletypeS_without_submodules=sigvalhash:'a->intvalhash_param:int->int->'a->inttype('a,'b)t(** We provide a [sexp_of_t] but not a [t_of_sexp] for this type because one needs to be
explicit about the hash and comparison functions used when creating a hashtable.
Note that [Hashtbl.Poly.t] does have [[@@deriving_inline sexp][@@@end]], and uses OCaml's built-in
polymorphic comparison and and polymorphic hashing. *)valsexp_of_t:('a->Sexp.t)->('b->Sexp.t)->('a,'b)t->Sexp.tincludeCreatorswithtype('a,'b)t:=('a,'b)t(** @inline *)includeAccessorswithtype('a,'b)t:=('a,'b)twithtype'akey='a(** @inline *)includeMultiwithtype('a,'b)t:=('a,'b)twithtype'akey:='akey(** @inline *)valhashable_s:('key,_)t->'keyKey.tincludeInvariant.S2withtype('a,'b)t:=('a,'b)tendmoduletypeS_poly=sigtype('a,'b)t[@@deriving_inlinesexp]includesig[@@@ocaml.warning"-32"]includePpx_sexp_conv_lib.Sexpable.S2withtype('a,'b)t:=('a,'b)tend[@@ocaml.doc"@inline"][@@@end]valhashable:'aHashable.tincludeInvariant.S2withtype('a,'b)t:=('a,'b)tincludeCreators_genericwithtype('a,'b)t:=('a,'b)twithtype'akey='awithtype('key,'data,'z)create_options:=('key,'data,'z)create_options_without_first_class_moduleincludeAccessorswithtype('a,'b)t:=('a,'b)twithtype'akey:='akeyincludeMultiwithtype('a,'b)t:=('a,'b)twithtype'akey:='akeyendmoduletypeFor_deriving=sigtype('k,'v)tmoduletypeSexp_of_m=sigtypet[@@deriving_inlinesexp_of]includesig[@@@ocaml.warning"-32"]valsexp_of_t:t->Ppx_sexp_conv_lib.Sexp.tend[@@ocaml.doc"@inline"][@@@end]endmoduletypeM_of_sexp=sigtypet[@@deriving_inlineof_sexp]includesig[@@@ocaml.warning"-32"]valt_of_sexp:Ppx_sexp_conv_lib.Sexp.t->tend[@@ocaml.doc"@inline"][@@@end]includeKey.Swithtypet:=tendvalsexp_of_m__t:(moduleSexp_of_mwithtypet='k)->('v->Sexp.t)->('k,'v)t->Sexp.tvalm__t_of_sexp:(moduleM_of_sexpwithtypet='k)->(Sexp.t->'v)->Sexp.t->('k,'v)tendmoduletypeHashtbl=sig(** A hash table is a mutable data structure implementing a map between keys and values.
It supports constant-time lookup and in-place modification.
{1 Usage}
As a simple example, we'll create a hash table with string keys using the
{{!create}[create]} constructor, which expects a module defining the key's type:
{[
let h = Hashtbl.create (module String);;
val h : (string, '_a) Hashtbl.t = <abstr>
]}
We can set the values of individual keys with {{!set}[set]}. If the key already has
a value, it will be overwritten.
{v
Hashtbl.set h ~key:"foo" ~data:5;;
- : unit = ()
Hashtbl.set h ~key:"foo" ~data:6;;
- : unit = ()
Hashtbl.set h ~key:"bar" ~data:6;;
- : unit = ()
v}
We can access values by key, or dump all of the hash table's data:
{v
Hashtbl.find h "foo";;
- : int option = Some 6
Hashtbl.find_exn h "foo";;
- : int = 6
Hashtbl.to_alist h;;
- : (string * int) list = [("foo", 6); ("bar", 6)]
v}
{{!change}[change]} lets us change a key's value by applying the given function:
{v
Hashtbl.change h "foo" (fun x ->
match x with
| Some x -> Some (x * 2)
| None -> None
);;
- : unit = ()
Hashtbl.to_alist h;;
- : (string * int) list = [("foo", 12); ("bar", 6)]
v}
We can use {{!merge}[merge]} to merge two hashtables with fine-grained control over
how we choose values when a key is present in the first ("left") hashtable, the
second ("right"), or both. Here, we'll cons the values when both hashtables have a
key:
{v
let h1 = Hashtbl.of_alist_exn (module Int) [(1, 5); (2, 3232)] in
let h2 = Hashtbl.of_alist_exn (module Int) [(1, 3)] in
Hashtbl.merge h1 h2 ~f:(fun ~key:_ -> function
| `Left x -> Some (`Left x)
| `Right x -> Some (`Right x)
| `Both (x, y) -> if x=y then None else Some (`Both (x,y))
) |> Hashtbl.to_alist;;
- : (int * [> `Both of int * int | `Left of int | `Right of int ]) list =
[(2, `Left 3232); (1, `Both (5, 3))]
v}
{1 Interface} *)includeS_without_submodules(** @inline *)moduletypeAccessors=AccessorsmoduletypeCreators=CreatorsmoduletypeKey=Key.S[@@deprecated"[since 2019-03] Use [Hashtbl.Key.S]"]moduletypeMulti=MultimoduletypeS_poly=S_polymoduletypeS_without_submodules=S_without_submodulesmoduletypeFor_deriving=For_derivingmoduleKey=Keytypenonrec('key,'data,'z)create_options=('key,'data,'z)create_optionsmoduleCreators(Key:sigtype'atvalhashable:'atHashable.tend):sigtype('a,'b)t_=('aKey.t,'b)tvalt_of_sexp:(Sexp.t->'aKey.t)->(Sexp.t->'b)->Sexp.t->('a,'b)t_includeCreators_genericwithtype('a,'b)t:=('a,'b)t_withtype'akey:='aKey.twithtype('key,'data,'a)create_options:=('key,'data,'a)create_options_without_first_class_moduleendmodulePoly:S_polywithtype('a,'b)t=('a,'b)t(** [M] is meant to be used in combination with OCaml applicative functor types:
{[
type string_to_int_table = int Hashtbl.M(String).t
]}
which stands for:
{[
type string_to_int_table = (String.t, int) Hashtbl.t
]}
The point is that [int Hashtbl.M(String).t] supports deriving, whereas the second
syntax doesn't (because [t_of_sexp] doesn't know what comparison/hash function to
use). *)moduleM(K:T.T):sigtypenonrec'vt=(K.t,'v)tendincludeFor_derivingwithtype('a,'b)t:=('a,'b)t(**/**)(*_ See the Jane Street Style Guide for an explanation of [Private] submodules:
https://opensource.janestreet.com/standards/#private-submodules *)modulePrivate:sigmoduletypeCreators_generic=Creators_generictypenonrec('key,'data,'z)create_options_without_first_class_module=('key,'data,'z)create_options_without_first_class_modulevalhashable:('key,_)t->'keyHashable.tendend