123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160openCoremoduleMake(Incr:Incremental.S_gen)=structmoduleE=Incr.Expert(* Hashtbl starts out at a ridiculous size of 128. This is a more reasonable number
of bins to have. *)lethashtbl_size=10(** [setup_generator] sets up the staged conversion.
[hashable] is used to build a table of necessary dependencies. We drop unnecessary
dependencies to allow them to be collected.
[compute_output key] returns the value of the output node corresponding to [key].
[compute_output] is closed over the mutable state determining the current value of
the selector's input.
[make_input_node] makes sure that when the mutable state inside [compute_output] is
updated, the corresponding nodes in the [necessary_dependencies] table are made
stale. *)letsetup_generator(hashable:'aHashtbl_intf.Hashable.t)~(compute_output:'a->'b)~(make_input_node:make_key_stale:('a->unit)->unitIncr.t):('a->'bIncr.t)Staged.t=letnecessary_dependencies=Hashtbl.Using_hashable.create~size:hashtbl_size~hashable()inlet(input_node:unitIncr.t)=letmake_key_stalekey=Hashtbl.findnecessary_dependencieskey|>Option.value~default:[]|>List.iter~f:E.Node.make_staleinmake_input_node~make_key_stalein(* Set the cutoff so that we never pass automatically from the update to the nodes. We
want everything to go through [make_key_stale] *)Incr.set_cutoffinput_nodeIncr.Cutoff.always;stage(funkey->letinput_dep=E.Dependency.createinput_nodeinletrecoutput_node=lazy(E.Node.create(fun()->compute_outputkey)~on_observability_change:(fun~is_now_observable->ifis_now_observablethenHashtbl.add_multinecessary_dependencies~key~data:(forceoutput_node)elseHashtbl.changenecessary_dependencieskey~f:(function|None->None|Somel->matchList.filterl~f:(Fn.non(phys_equal(forceoutput_node)))with|[]->None|l'->Somel')))inletoutput_node=forceoutput_nodeinE.Node.add_dependencyoutput_nodeinput_dep;E.Node.watchoutput_node)(** This creates a unit incremental that fires whenever the input incremental
fires. When that occurs, it updates [selected] to match the current value of
[input], and calls [make_key_stale] for both the old and new value of the
incremental. *)letupdate_one~input~selected~make_stale=Incr.mapinput~f:(funinp->Option.iterinp~f:make_stale;Option.iter!selected~f:make_stale;selected:=inp)letselect_one'(typea)(moduleH:Hashable.Commonwithtypet=a)(input:aoptionIncr.t)=letselected=refNoneinletcompute_outputkey=match!selectedwith|None->false|Somekey'->H.comparekeykey'=0inletmake_input_node~make_key_stale=update_one~input~selected~make_stale:make_key_staleinsetup_generatorH.hashable~compute_output~make_input_nodeletselect_onehinput=select_one'h(Incr.map~f:Option.someinput)letselect_one_value'(typea)(moduleH:Hashable.Commonwithtypet=a)~defaultinput=letselected=refNoneinletcompute_outputkey=match!selectedwith|None->default|Some(key',data)->ifH.comparekeykey'=0thendataelsedefaultinletmake_input_node~make_key_stale=update_one~selected~input~make_stale:(fun(key,_)->make_key_stalekey)insetup_generatorH.hashable~compute_output~make_input_nodeletselect_one_valueh~defaultinput=select_one_value'h~default(Incr.map~f:Option.someinput)letselect_many_values(typea)(moduleH:Hashable.Commonwithtypet=a)~defaultinput=lethashable=H.hashableinletselected=Hashtbl.Using_hashable.create~size:hashtbl_size~hashable()inletcompute_outputkey=Hashtbl.findselectedkey|>Option.value~defaultinletmake_input_node~make_key_stale=Incr.mapinput~f:(funinp->Hashtbl.iter_keysselected~f:make_key_stale;Hashtbl.clearselected;List.iterinp~f:(fun(key,data)->make_key_stalekey;Hashtbl.setselected~key~data))insetup_generatorhashable~compute_output~make_input_nodeletselect_many(typea)(moduleH:Hashable.Commonwithtypet=a)input=lethashable=H.hashableinletselected=Hash_set.Using_hashable.create~size:hashtbl_size~hashable()inletcompute_outputkey=Hash_set.memselectedkeyinletmake_input_node~make_key_stale=Incr.mapinput~f:(funinp->letold_set=Hash_set.copyselectedinHash_set.clearselected;List.iterinp~f:(funkey->ifnot(Hash_set.memold_setkey)thenmake_key_stalekey;Hash_set.addselectedkey);Hash_set.iterold_set~f:(funkey->ifnot(Hash_set.memselectedkey)thenmake_key_stalekey;))insetup_generatorhashable~compute_output~make_input_nodeend