Parameter Make.1-Incr
- type 'a t
- type 'a tis the type of incrementals that have a value of type- 'a.- Incrementals are not covariant, i.e. we do not have - +'a t-- consider, e.g.- set_cutoffand- get_cutoff. However, if you have types- a1and- a2where- a1is a subtype of- a2, and a value- t1 : a1 t, then the following builds an incremental value of type- a2 t:- let t2 : a2 t = t1 >>| fun a1 -> (a1 : a1 :> a2)
- val sexp_of_t : ('a -> Ppx_sexp_conv_lib.Sexp.t) -> 'a t -> Ppx_sexp_conv_lib.Sexp.t
- type 'a incremental- = 'a t
include Core_kernel.Invariant.S1 with type 'a t := 'a t
- val invariant : 'a Base__.Invariant_intf.inv -> 'a t Base__.Invariant_intf.inv
- val is_const : _ t -> bool
- If - is_const tthen- tis a constant-valued incremental.- is_const (const a)is true.
Creating incrementals
- val const : 'a -> 'a t
- const vreturns an incremental whose value never changes. It is the same as- return, but reads more clearly in many situations because it serves as a nice reminder that the incremental won't change (except possibly be invalidated).
- val return : 'a -> 'a t
- val map : 'a t -> f:('a -> 'b) -> 'b t
- map t1 ~freturns an incremental- tthat maintains its value as- f a, where- ais the value of- t1.- map2,- map3, ...,- map9are the generalizations to more arguments. If you need map<N> for some N > 9, it can easily be added, but also see- array_foldand- unordered_array_fold.- fshould not create incremental nodes but this behavior is not checked; if you want to create incremental nodes, use- bind. The invalidation machinery that is used with- bindis not used with- map.
- val (>>|) : 'a t -> ('a -> 'b) -> 'b t
- val map2 : 'a1 t -> 'a2 t -> f:('a1 -> 'a2 -> 'b) -> 'b t
- val map3 : 'a1 t -> 'a2 t -> 'a3 t -> f:('a1 -> 'a2 -> 'a3 -> 'b) -> 'b t
- val map4 : 'a1 t -> 'a2 t -> 'a3 t -> 'a4 t -> f:('a1 -> 'a2 -> 'a3 -> 'a4 -> 'b) -> 'b t
- val map5 : 'a1 t -> 'a2 t -> 'a3 t -> 'a4 t -> 'a5 t -> f:('a1 -> 'a2 -> 'a3 -> 'a4 -> 'a5 -> 'b) -> 'b t
- val map6 : 'a1 t -> 'a2 t -> 'a3 t -> 'a4 t -> 'a5 t -> 'a6 t -> f:('a1 -> 'a2 -> 'a3 -> 'a4 -> 'a5 -> 'a6 -> 'b) -> 'b t
- val map7 : 'a1 t -> 'a2 t -> 'a3 t -> 'a4 t -> 'a5 t -> 'a6 t -> 'a7 t -> f:('a1 -> 'a2 -> 'a3 -> 'a4 -> 'a5 -> 'a6 -> 'a7 -> 'b) -> 'b t
- val map8 : 'a1 t -> 'a2 t -> 'a3 t -> 'a4 t -> 'a5 t -> 'a6 t -> 'a7 t -> 'a8 t -> f:('a1 -> 'a2 -> 'a3 -> 'a4 -> 'a5 -> 'a6 -> 'a7 -> 'a8 -> 'b) -> 'b t
- val map9 : 'a1 t -> 'a2 t -> 'a3 t -> 'a4 t -> 'a5 t -> 'a6 t -> 'a7 t -> 'a8 t -> 'a9 t -> f:('a1 -> 'a2 -> 'a3 -> 'a4 -> 'a5 -> 'a6 -> 'a7 -> 'a8 -> 'a9 -> 'b) -> 'b t
- val bind : 'a t -> f:('a -> 'b t) -> 'b t
- bind t1 ~freturns an incremental- t2that behaves like- f v, where- vis the value of- t1. If- t1's value changes, then incremental applies- fto that new value and- t2behaves like the resulting incremental.- bindcan be significantly more expensive than- mapduring stabilization, because, when its left-hand side changes, it requires modification of the incremental DAG, while- mapsimply flows values along the DAG. Thus it is preferable to use- map(and its n-ary variants above) instead of- bindunless one actually needs- bind's power.- bind2 t1 t2 ~fis:- bind (map2 t1 t2 ~f:(fun v1 v2 -> (v1, v2))) ~f:(fun (v1, v2) -> f v1 v2)- This is equivalent to - bind t1 ~f:(fun v1 -> bind t2 ~f:(fun v2 -> f v1 v2))but more efficient due to using one bind node rather than two. The other- bind<N>functions are generalize to more arguments.
- val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
- val bind2 : 'a1 t -> 'a2 t -> f:('a1 -> 'a2 -> 'b t) -> 'b t
- val bind3 : 'a1 t -> 'a2 t -> 'a3 t -> f:('a1 -> 'a2 -> 'a3 -> 'b t) -> 'b t
- val bind4 : 'a1 t -> 'a2 t -> 'a3 t -> 'a4 t -> f:('a1 -> 'a2 -> 'a3 -> 'a4 -> 'b t) -> 'b t
module Infix : sig ... end- val join : 'a t t -> 'a t
- join treturns an incremental that behaves like the incremental that- tcurrently holds.
- val if_ : bool t -> then_:'a t -> else_:'a t -> 'a t
- if_ tb ~then_ ~else_returns an incremental- tthat holds the value of- then_if- tbis true, the value of- else_if- tbis false. Note that- tonly depends on one of- then_or- else_at a time, i.e.- if_ tb ~then_ ~elseis like:- bind b ~f:(fun b -> if b then then_ else else_)- which is not the same as: - map3 b then_ else_ ~f:(fun b then_ else_ -> if b then then_ else else_)
- val freeze : ?when_:('a -> bool) -> 'a t -> 'a t
- freeze ?when_ treturns an incremental whose value is- t's value- vuntil the first stabilization in which- when_ vholds, at which point the freeze node's value becomes constant and never changes again. Calling- freeze tforces- tto be necessary until it freezes regardless of whether the freeze node is necessary, but not thereafter (although of course- tcould remain necessary for other reasons). The result of- freeze t, once frozen, will never be invalidated, even if- tis invalidated, and even if the scope in which the freeze is created is invalidated. However, prior to- when_ vbecoming true,- freeze tcan be invalidated.
- val depend_on : 'a t -> depend_on:_ t -> 'a t
- depend_on input ~depend_onreturns an- outputwhose value is the same as- input's value, such that- depend_onis necessary so long as- outputis necessary. It is like:- map2 input depend_on ~f:(fun a _ -> a)- but with a cutoff such that - output's value only changes when- input's value changes.
- val necessary_if_alive : 'a t -> 'a t
- necessary_if_alive inputreturns- outputthat has the same value and cutoff as- input, such that as long as- outputis alive,- inputis necessary.
- val for_all : bool t array -> bool t
- for_all tsreturns an incremental that is- trueiff all- tsare- true.
- val exists : bool t array -> bool t
- exists tsreturns an incremental that is- trueiff at least one of the- tsis- true.
- val all : 'a t list -> 'a list t
- all tsreturns an incremental whose value is a list of the values of all of the- ts. In any stabilization where any of the- tschanges, the entire list is recreated (once all of the- tshave stabilized). This essentially an- array_foldover the- ts.
- val both : 'a t -> 'b t -> ('a * 'b) t
- both t1 t2returns an incremental whose value is pair of values of- t1and- t2. Both- map (both t1 t2) ~fand- map2 t1 t2 ~f:(fun a1 a2 -> f (a1, a2))return an incremental with the same behavior, but the- map2version is more efficient, because it creates a single node, whereas the- bothversion creates two nodes.
Array folds and sums
- val array_fold : 'a t array -> init:'b -> f:('b -> 'a -> 'b) -> 'b t
- array_fold ts ~init ~fcreates an incremental- twhose value is:- Array.fold ts ~init ~f:(fun ac t -> f ac (value t))- In a stabilization during which any of the - tschanges, the entire fold will be computed once all of the- tshave been computed.
- val reduce_balanced : 'a t array -> f:('a -> 'b) -> reduce:('b -> 'b -> 'b) -> 'b t option
- reduce_balanced ts ~f ~reducedoes a fold-like operation over- ts. Unlike- array_fold, the operation will be computed in- O(min(n, k * log(n)))time, where- nis the size of- tsand- kis the number of elements of- tsthat have changed since the last stabilization.- Generally, if most or all of - tsare changing between stabilizations, using- array_foldwill have better constant factors.- The - reduceargument must be an associative operation:- reduce a (reduce b c) = (reduce (reduce a b) c).- Noneis returned upon supplying an empty array.
- val unordered_array_fold : ?full_compute_every_n_changes:int -> 'a t array -> init:'b -> f:('b -> 'a -> 'b) -> f_inverse:('b -> 'a -> 'b) -> 'b t
- unordered_array_fold ts ~init ~f ~f_inversefolds over the- ts. Unlike- array_fold, the fold will be computed in time proportional to the number of- tsthat change rather than the number of- ts. In a stabilization, for each- tin- tsthat changes from- old_valueto- new_value, the value of the unordered-array fold will change from- bto- f (f_inverse b old_value) new_value. The- t's that change may take effect in any order.- If repeated changes might accumulate error, one can cause the fold to be fully computed after every - full_compute_every_n_changeschanges. If you do not supply- full_compute_every_n_changes, then full computes will never happen after the initial one.- opt_unordered_array_foldis like- unordered_array_fold, except that its result is- Someiff all its inputs are- Some.
- val opt_unordered_array_fold : ?full_compute_every_n_changes:int -> 'a option t array -> init:'b -> f:('b -> 'a -> 'b) -> f_inverse:('b -> 'a -> 'b) -> 'b option t
- val sum : ?full_compute_every_n_changes:int -> 'a t array -> zero:'a -> add:('a -> 'a -> 'a) -> sub:('a -> 'a -> 'a) -> 'a t
- sum ts ~zero ~add ~sub ?full_compute_every_n_changesreturns an incremental that maintains the sum of the- ts. It uses- unordered_array_foldso that the work required to maintain the sum is proportional to the number of- tsthat change (i.e. one- suband one- addper change).- opt_sumis like- sum, except that its result is- Someiff all its inputs are- Some.
Variables
module Var : sig ... endObservers
- module Observer : sig ... end
- An observer lets one get the value of an incremental, either by asking directly for the value or installing an on-update handler to run when the incremental's value changes. 
- val observe : ?should_finalize:bool -> 'a t -> 'a Observer.t
- observe treturns a new observer for- t.- observeraises if called during stabilization.- By default, an observer has a finalizer that calls - disallow_future_usewhen the observer is no longer referenced. One can use- ~should_finalize:falseto cause the finalizer to not be created, in which case the observer will live until- disallow_future_useis explicitly called.
- module Update : sig ... end
- on_update t ~fis similar to- Observer.on_update_exn, but it does not cause- tto be necessary. Instead of the- Initializedupdate, there are updates for when a node becomes- Necessaryor- Unnecessary. Here is a state diagram for the allowable sequences of- Update.t's that can be supplied to a particular- f:
Stabilization
Cutoffs
- module Cutoff : sig ... end
- An - 'a Cutoff.tis a function that returns- trueif propagation of changes should be cutoff at a node based on the old value and the (possible) new value of the node.
- val set_cutoff : 'a t -> 'a Cutoff.t -> unit
- set_cutoff t cutoffreplaces the current cutoff function for- twith- cutoff.- cutoffwill be called any time- tis recomputed, with- old_valuebeing the value of- tbefore the recomputation and- new_valuebeing the value that just recomputed. If- cutoff ~old_value ~new_value, then- t's value will remain as- old_value(- new_valueis discarded) and anything depending on- twill not be recomputed (at least not because of- t). If- not (cutoff ~old_value ~new_value), then- t's value will become- new_value, and all nodes depending on- twill recomputed.- A reasonable choice for - cutoffis an equality function on- 'a.- The default cutoff for every node is - phys_equal. For example, this means that a- unit incrementalwould only fire once; to disable this, use- set_cutoff t Cutoff.never.
- module Scope : sig ... end
- val lazy_from_fun : (unit -> 'a) -> 'a Core_kernel.Lazy.t
- lazy_from_fun fis like- Lazy.from_fun f, except that the nodes created by- fwill be created in the scope in which- lazy_from_funwas called, rather than in the scope of the piece of code that first forces the resulting lazy. Not using this function when defining lazy values is likely to result in exceptions being thrown by incremental. As a rule of thumb, all- lazy ethat might create incremental nodes should be replaced by- lazy_from_fun (fun () -> e).- As usual with - Lazy, if- fraises, then that exception will be raised when calling- Lazy.force.
- val default_hash_table_initial_size : int
- val memoize_fun : ?initial_size:int -> 'a Core_kernel.Hashtbl.Hashable.t -> ('a -> 'b) -> ('a -> 'b) Core_kernel.Staged.t
- memoize_fun f hashablereturns a function- mthat is a memoized version of- fthat will run- f aon each distinct- athat- mis applied to, memoize the result (in a hash table), and thereafter for- a,- mwill return the memoized result.- When - mis called, it uses- Scope.withinto run- fin the scope that was in effect when- memoize_fun fwas called. This is essential to correctly capture the dependence of nodes that- fcreates on values that- fis closed over, which may in turn depend on the left-hand sides of binds in the scope in effect when- memoize_fun fwas called. Furthermore, nodes that- fcreates do not depend on the scope in effect when- mis called.- memoize_fun_by_keyis a generalization that allows one to memoize over values that contain a uniquely identifying key, but also have other data.
- val memoize_fun_by_key : ?initial_size:int -> 'key Core_kernel.Hashtbl.Hashable.t -> ('a -> 'key) -> ('a -> 'b) -> ('a -> 'b) Core_kernel.Staged.t
- val user_info : _ t -> Core_kernel.Info.t option
- For debugging purposes, one can store an arbitrary - Info.tin a node. This will be displayed as part of a node in error messages.
- val set_user_info : _ t -> Core_kernel.Info.t option -> unit
- module Expert : sig ... end
- A low-level, experimental interface to incremental. This is useful when you need more control over the dependency graph, for performance reasons. It comes at the cost that it's much harder to use right. Specifically, here is what you can do with an expert node: 
- module State : sig ... end
module Packed : sig ... end- val pack : _ t -> Packed.t
- val save_dot : string -> unit
- save_dot fileoutputs to- filethe DAG of all necessary nodes, in dot format.
- val keep_node_creation_backtrace : bool Core_kernel.ref
- If - keep_node_creation_backtrace, then whenever a new node is created, incremental will call- Backtrace.getand store the result in the node. The backtrace will then appear in subsequent error messages when the node is pretty printed.
- module Let_syntax : sig ... end
- This - Let_syntaxallows you to write expressions like
Time
Incremental has a timing-wheel-based clock, and lets one build incremental values that change as its time passes. One must explicitly call advance_clock to change incremental's clock; there is no implicit call based on the passage of time.
module Before_or_after : sig ... endmodule Clock : sig ... end- val weak_memoize_fun : ?initial_size:int -> 'a Core_kernel.Hashtbl.Hashable.t -> ('a -> 'b Core_kernel.Heap_block.t) -> ('a -> 'b Core_kernel.Heap_block.t) Core_kernel.Staged.t
- The weak versions of the memoization functions use a - Weak_hashtblfor the memo table. This keeps a weak pointer to each result, and so the garbage collector automatically removes unused results. Furthermore,- stabilizeremoves the table entries whose result is unused.
- val weak_memoize_fun_by_key : ?initial_size:int -> 'key Core_kernel.Hashtbl.Hashable.t -> ('a -> 'key) -> ('a -> 'b Core_kernel.Heap_block.t) -> ('a -> 'b Core_kernel.Heap_block.t) Core_kernel.Staged.t