-
Notifications
You must be signed in to change notification settings - Fork 61
/
incremental_intf.ml
2013 lines (1627 loc) · 73.6 KB
/
incremental_intf.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
(** For expressing a computation that depends on variables and that can automatically
incrementally recompute after the values of some of the variables change.
{1 Incremental in a nutshell}
Incremental is used to define a collection of interdependent values, some of which are
"variables" set by user code and others that are defined via functions (in the
mathematical and programming senses) of other incremental values. Incremental
automatically tracks all the dependencies between incremental values and can, on
demand, propagate changed variables and recompute the incremental values that depend
on them.
To use incremental, one first creates a new instance via:
{[
module Inc : Incremental.S = Incremental.Make ()
]}
The functor application creates data structures that will be shared throughout the
lifetime of all incremental values used with this instance. Since [Incremental.Make]
is a generative functor, the type system enforces that different applications of the
functor deal with disjoint sets of incrementals.
For the remainder of this comment, we assume a particular [Inc] is [open]:
{[
open Inc
]}
As an example of a simple computation, suppose we have integer variables [x] and [y]
and want to keep an incremental value [z] defined by [z = x + y]. We could do this
with:
{[
let x = Var.create 13
let y = Var.create 17
let z = map2 (Var.watch x) (Var.watch y) ~f:(fun x y -> x + y)
]}
With this, as [x] and [y] change, incremental can recompute [z = x + y] on demand.
Incremental only recomputes values that are being "observed", which one indicates by
calling the [observe] function to get an "observer", e.g.:
{[
let z_o = observe z
]}
Incremental doesn't compute [z] every time [x] and [y] change. Rather, one must
explicitly tell incremental when one wants [z] (and all other observed values) to be
brought up to date, by calling [stabilize]:
{[
stabilize ();
]}
At this point, the value of [z] is [30], which we can verify by:
{[
assert (Observer.value_exn z_o = 30);
]}
If we change the value of [x] and then tell incremental to recompute observed values,
then the value of [z] will change appropriately:
{[
Var.set x 19;
stabilize ();
assert (Observer.value_exn z_o = 36);
]}
Another way to observe values is to use [Observer.on_update_exn], which attaches an
"on-update handler" to an observer -- the handler will be run after each stabilization
in which the observer's value changed (or was initialized) during stabilization.
User functions given to incremental should never raise any exceptions: doing so will
cause all future calls to [stabilize] to raise.
{1 The incremental DAG}
One can think of incrementals as forming a directed acyclic graph (DAG), where nodes
correspond to incremental values and there is an edge from node [n1] to node [n2] if
the value of [n2] depends on the value of [n1]. For example, the DAG for the above
example has an edge from [x] to [z] and from [y] to [z]. The graph must be acyclic in
order for the computation to be well defined. The graph is a DAG rather than a tree
because incremental values can be shared. Extending the above example, we might have:
{[
let w = map2 (Var.watch y) z ~f:(fun y z -> y - z)
]}
Both the node for [y] and the node for [z] are shared.
We will use "node" to mean "incremental value" when we want to emphasize some aspect
of the DAG.
Say that a node is "observed" if there is an observer for it (created via [observe]).
Say that a node is "necessary" if there is a path from that node to an observed node.
[stabilize] ensures that all necessary nodes have correct values; it will not compute
unnecessary nodes. An unobserved node becomes necessary by a call to [observe] or by
being used to compute an observed node; this will cause the appropriate DAG edges to
be added. A necessary node will become unnecessary if its observer (if any) becomes
unused and if the node is no longer used to compute any observed nodes. This will
cause the appropriate DAG edges to be removed.
Incremental does not know whether user-supplied functions (e.g. functions supplied to
[bind] or [map]) are side effecting, and will not evaluate them for side effect. If
the resulting incrementals are not necessary then the function will not be called.
{1 Stabilization}
[stabilize] traverses the DAG in topological order starting at variables that changed
since the last stabilization and recomputing their dependents. This is done by using
a "recompute heap" to visit the nodes in non-decreasing order of "height", which is a
over-approximation of the longest path from a variable to that node. To ensure that
each node is computed at most once and that its children are stabilized before it is
computed, nodes satisfy the property that if there is an edge from n1 to n2, then the
height of n1 is less than the height of n2.
[stabilize] repeats the following steps until the heap becomes empty:
1. remove from the recompute heap a node with the smallest height
2. recompute that node
3. if the node's value changes, then add its parents to the heap.
The definition of "changes" in step (3) is configurable by user code. By default, a
node is considered to change if its new value is not [phys_equal] to the previous
value. One can use [set_cutoff] on a node to change its cutoff function, e.g. for
[floats] one could cutoff propagation if the old value and new value are closer than
some threshold.
If [stabilize] ever raises due to an error, then the incremental system becomes
unusable, and all future calls to [stabilize] will immediately raise.
Stabilization uses a heap implemented with an array whose length is the max height, so
for good performance, the height of nodes must be small. There is an upper bound on
the height of nodes, [max_height_allowed], which defaults to 128. An attempt to
create a node with larger height will raise. One can dynamically increase
[max_height_allowed]; however, one should be wary of doing so, for performance
reasons.
{1 Bind}
Much of the power of incremental comes from [bind], also written [>>=]. As a
reminder, [bind] has this type:
{[
val bind : 'a t -> f:('a -> 'b t) -> 'b t
]}
[bind ta ~f] returns an incremental [tb] that behaves like [f a], where [a] is the
most recent value of [ta]. The implementation only calls [f] when the value of [ta]
changes. Thinking in terms of the DAG, [bind ta ~f] returns a node [tb] such that
whenever the value of [ta] changes, the implementation calls [f] to obtain a node
(possibly with an arbitrary DAG below it) that defines the value of [tb].
[bind] can be used to transition existing parts of the graph between necessary and
unnecessary. E.g.:
{[
val if_ : bool t -> a t -> a t -> a t
let if_ a b c = bind a ~f:(fun a -> if a then b else c)
]}
With [let t = if_ a b c], when [a] is [true], if [t] is necessary, then [b] will be
necessary, but [c] will not. And vice-versa when [a] is [false].
Even more, [bind] allows one to dynamically create an arbitrary graph based on the
value of some other incremental, and to "hide" that dynamism behind an ordinary
incremental value. One common way to use this is for dynamic reconfiguration, e.g.:
{[
let config_var = Var.create config in
bind (Var.watch config_var) ~f:(fun config -> ... )
]}
Then, whenever one wants to reconfigure the system, one does [Var.set config_var]
and then [stabilize], which will construct a new DAG according to the new config.
Bind nodes introduce special height constraints, so that stabilization is guaranteed
to recompute the left-hand side of a bind before recomputing any node created by the
right-hand side [f]. This avoids recomputing nodes created on the right-hand side
that would then become unnecessary when the left-hand side changes. More precisely,
in [t >>= f], any node created by [f] is made to have a height larger than [t]. This
rule applies also to bind nodes created by [f], so that ultimately the height of every
node is greater than the height of all the left-hand sides of the binds that were
involved in its creation. The height requirement does not apply to nodes returned by
[f] but not created by [f] -- such nodes depend on the bind in effect when they were
created, but have no dependence on [t].
When the left-hand side of a bind node changes, stabilization "invalidates" all the
nodes that depend on it (because they may use an old value of the left-hand side).
For example, consider:
{[
let t1 = map ... in
bind t2 ~f:(fun _ ->
let t3 = map ... in
map2 t1 t3 ~f:(...))
]}
In this example, [t1] is created outside of [bind t2], whereas [t3] is created by the
right-hand side of [bind t2]. So, [t3] depends on [t2] (and has a greater height),
whereas [t1] does not. And, in a stabilization in which [t2] changes, we are
guaranteed to not recompute the old [t3], but we have no such guarantee about [t1].
Furthermore, when [t2] changes, the old [t3] will be invalidated, whereas [t1] will
not.
Since [bind] essentially allows one to add arbitrary edges to the DAG, one can use it
to construct a cycle. [stabilize] will detect such cycles and raise.
{1 Garbage collection}
Incremental maintains three kinds of pointers:
- from nodes to their children (nodes they depend on).
- from necessary nodes to their necessary parents (nodes that depend on them).
- from observers to the nodes they observe.
So, all necessary nodes are kept alive, from the perspective of the garbage collector.
If an observer has no on-update handlers and user code no longer holds on to it,
incremental (via a finalizer on the observer) detects this and disallows future use of
the observer, making the node it observed unnecessary if it is not necessary for
another reason. One can eagerly remove an observer by calling [disallow_future_use].
Because finalizers may be called much later than when an observer actually becomes
unreachable, it is preferable to disable observers using [disallow_future_use] to
avoid useless propagation during stabilizations.
If an observer has on-update handlers, calling [disallow_future_use] is the only way
to have it removed.
{1 The implementation}
The key type in the implementation is [Node.t], which represents one node in the
incremental DAG. The node type is in fact the same as [Incremental.t], although this
type equivalence is not exposed. A node is a record with many fields (> 20). In
particular a node holds:
- kind -- the kind of node it is (const, var, map, bind, snapshot, etc.).
- value -- the node's current value.
- recompute id -- the stabilization number at which it was last recomputed.
- change id -- the stabilization number at which its value last changed.
- height -- the height of the node in the DAG.
- parents -- the necessary nodes that depend on it.
- children -- the nodes that it depends on.
- created_in -- the scope in which it was created.
Say that a node is "stale" if it has never been computed or if its recompute id is
less than the change id of one of its children. A node should be recomputed if it
is both necessary and stale.
The [State.t] type holds all the mutable data used to implement stabilization. In
particular, the incremental state contains:
- the current stabilization number
- the set of observers
- a recompute heap -- nodes that need to be recomputed, ordered by height.
- an adjust-heights heap -- nodes whose height needs increasing, ordered by height.
The goals of stabilization are to:
- compute all necessary nodes that need to be recomputed.
- only compute necessary nodes.
- compute each node at most once.
- only compute a node after ensuring its children are up to date.
To do this, incremental maintains the following invariants:
- [p] is in [c]'s parents iff ([c] is in [p]'s children && [p] is necessary)
- [p] is in the recompute heap iff [p] is necessary and stale.
- if [p] is a parent of [c], then [p]'s height is greater than [c]'s height.
The first invariant ensures that when a node's value changes, we can reach from it all
necessary nodes (and only the necessary nodes) that depend on it. The second
invariant ensures that that stabilization only computes necessary nodes. The third
invariant, combined with the fact that stabilization always recomputes a node from the
recompute-heap that has minimum height, ensures that we only compute a node after all
its children are stable, and that we compute each node at most once.
Finally, at the end of stabilization, the recompute heap is empty, so the invariant
implies that there are no necessary nodes that are stale, i.e. stabilization has
computed all necessary nodes that need to be recomputed.
{1 Maintaining the parent invariant}
Maintaining the invariant that a node has edges only to necessary parents requires
traversing a node's descendants when it transitions between necessary and unnecessary,
in order to add or remove parents as appropriate. For example, when an observer is
first added to an unnecessary node, the implementation visits all its descendants to
add parents. This is essentially a form of ref-counting, in which the counter is the
number of parents that a node has. There is no problem with cycles because the DAG
requirement on the graph is enforced.
{1 Maintaining the height invariant and checking for cycles}
Maintaining the invariant that a necessary node's height is larger than all of its
children requires adjusting heights when an edge is added to the DAG (e.g. when a bind
left-hand side changes). This is done using the "adjust-heights" heap. When an edge
is added, if the child's height is greater than or equal to the parent's height, then
the adjust-heights heap increases the height of the parent and all of the parent's
ancestors as necessary in order to restore the height invariant. This is done by
visiting ancestors in topological order, in increasing order of pre-adjusted height.
If during that traversal, the child of the original edge is visited, then there is a
cycle in the graph, and stabilization raises.
In pathological situations, the implementation will raise due to a cyclic graph even
though subsequent graph operations would eliminate the cycle. This is because the
cyclicity check happens after each edge is added, rather than waiting until a batch
of graph changes.
{1 Bind, scopes, and invalidation}
Much of the complexity of the implementation comes from [bind]. In [t >>= f], when
[f] is applied to the value of [t], all of the nodes that are created depend on that
value. If the value of [t] changes, then those nodes no longer make sense because
they depend on a stale value. It would be both wasteful and wrong to recompute any of
those "invalid" nodes. So, the implementation maintains the invariant that the height
of a necessary node is greater than the height of the left-hand side of the nearest
enclosing bind. That guarantees that stabilization will stabilize the left-hand side
before recomputing any nodes created on the right-hand side. Furthermore, if the
left-hand side's value changes, stabilization marks all the nodes on the right-hand
side as invalid. Such invalid nodes will typically be unnecessary, but there are
pathological cases where they remain necessary.
The bind height invariant is accomplished using a special "bind-lhs-change" node,
which is a parent of the bind-lhs and a child of the bind result. The incremental
state maintains the "current scope", which is the bind whose right-hand side is
currently being evaluated, or a special "top" scope if there is no bind in effect.
Each node has a [created_in] field set to the scope in effect when the node is
created. The implementation keeps for each scope, a singly-linked list of all nodes
created in that scope. Invalidation traverses this list, and recurs on bind nodes in
it to traverse their scopes as well.
[if_] and [join] are special cases of [bind] that manipulate the graph; however they
do not create new scopes. They use a similar lhs-change node to detect changes and
perform graph manipulation.
{1 Debugging}
For performance reasons, [Incremental] is built with debugging asserts disabled.
[Incremental_debug] is a library that uses the same code as [Incremental], but has
debugging asserts enabled (via an [IFDEF]). [Incremental_debug] is significantly
slower than [Incremental], but may detect a bug in the Incremental library that would
otherwise remain undetected by [Incremental].
{1 Reading guide}
Here's a breakdown of the modules in roughly dependency order.
{ul
{li [Import] -- imports from other libraries, and commonly used functions }
{li Basic types.
- [Cutoff] -- a cutoff function
- [On_update_handler] -- a function to run when a node's value changes
- [Node_id] -- an integer unique id for nodes
- [Raised_exn] -- a wrapper around [exn] that keeps a backtrace.
- [Sexp_of] -- interfaces for types that have [with sexp_of].
- [Stabilization_num] -- an abstract [int option], used to express the stabilization
cycle when something happens. }
{li [Types] -- mutually recursive types.
Many of the types used in the implementation are mutually recursive. They are
all defined in [Types]. Each type is then later defined in its own module, along
with [with fields, sexp]. }
{li [Kind] -- the variant with one constructor for each kind of node, plus a special
constructor for invalidated nodes. Many of the value-carrying variants also have a
module for its argument type:
- [Array_fold]
- [At]
- [At_intervals]
- [Bind]
- [Freeze]
- [If_then_else]
- [Join]
- [Snapshot]
- [Step_function_node]
- [Unordered_array_fold]
- [Var] }
{li [Scope] -- a packed bind. }
{li [Node] -- the main node type. }
{li [Internal_observer] }
{li [Observer] -- a [ref] wrapper around [Internal_observer], used so a finalizer
can detect when user code is done with an observer. }
{li [Recompute_heap] }
{li [Adjust_heights_heap] }
{li [Alarm_value] -- values stored in the timing wheel, for time-based nodes. }
{li [State] -- the record type will all data structures used for stabilization, and
the implementation of all the [Incremental] functions. }
{li [Incremental], the main functor, mostly a wrapper around [State]. }
{li [Incremental_unit_tests]. }
} *)
open Core
open! Import
module type Map_n_gen = sig
type ('a, 'w) t
val map2 : ('a1, 'w) t -> ('a2, 'w) t -> f:('a1 -> 'a2 -> 'b) -> ('b, 'w) t
val map3
: ('a1, 'w) t
-> ('a2, 'w) t
-> ('a3, 'w) t
-> f:('a1 -> 'a2 -> 'a3 -> 'b)
-> ('b, 'w) t
val map4
: ('a1, 'w) t
-> ('a2, 'w) t
-> ('a3, 'w) t
-> ('a4, 'w) t
-> f:('a1 -> 'a2 -> 'a3 -> 'a4 -> 'b)
-> ('b, 'w) t
val map5
: ('a1, 'w) t
-> ('a2, 'w) t
-> ('a3, 'w) t
-> ('a4, 'w) t
-> ('a5, 'w) t
-> f:('a1 -> 'a2 -> 'a3 -> 'a4 -> 'a5 -> 'b)
-> ('b, 'w) t
val map6
: ('a1, 'w) t
-> ('a2, 'w) t
-> ('a3, 'w) t
-> ('a4, 'w) t
-> ('a5, 'w) t
-> ('a6, 'w) t
-> f:('a1 -> 'a2 -> 'a3 -> 'a4 -> 'a5 -> 'a6 -> 'b)
-> ('b, 'w) t
val map7
: ('a1, 'w) t
-> ('a2, 'w) t
-> ('a3, 'w) t
-> ('a4, 'w) t
-> ('a5, 'w) t
-> ('a6, 'w) t
-> ('a7, 'w) t
-> f:('a1 -> 'a2 -> 'a3 -> 'a4 -> 'a5 -> 'a6 -> 'a7 -> 'b)
-> ('b, 'w) t
val map8
: ('a1, 'w) t
-> ('a2, 'w) t
-> ('a3, 'w) t
-> ('a4, 'w) t
-> ('a5, 'w) t
-> ('a6, 'w) t
-> ('a7, 'w) t
-> ('a8, 'w) t
-> f:('a1 -> 'a2 -> 'a3 -> 'a4 -> 'a5 -> 'a6 -> 'a7 -> 'a8 -> 'b)
-> ('b, 'w) t
val map9
: ('a1, 'w) t
-> ('a2, 'w) t
-> ('a3, 'w) t
-> ('a4, 'w) t
-> ('a5, 'w) t
-> ('a6, 'w) t
-> ('a7, 'w) t
-> ('a8, 'w) t
-> ('a9, 'w) t
-> f:('a1 -> 'a2 -> 'a3 -> 'a4 -> 'a5 -> 'a6 -> 'a7 -> 'a8 -> 'a9 -> 'b)
-> ('b, 'w) t
val map10
: ('a1, 'w) t
-> ('a2, 'w) t
-> ('a3, 'w) t
-> ('a4, 'w) t
-> ('a5, 'w) t
-> ('a6, 'w) t
-> ('a7, 'w) t
-> ('a8, 'w) t
-> ('a9, 'w) t
-> ('a10, 'w) t
-> f:('a1 -> 'a2 -> 'a3 -> 'a4 -> 'a5 -> 'a6 -> 'a7 -> 'a8 -> 'a9 -> 'a10 -> 'b)
-> ('b, 'w) t
val map11
: ('a1, 'w) t
-> ('a2, 'w) t
-> ('a3, 'w) t
-> ('a4, 'w) t
-> ('a5, 'w) t
-> ('a6, 'w) t
-> ('a7, 'w) t
-> ('a8, 'w) t
-> ('a9, 'w) t
-> ('a10, 'w) t
-> ('a11, 'w) t
-> f:
('a1
-> 'a2
-> 'a3
-> 'a4
-> 'a5
-> 'a6
-> 'a7
-> 'a8
-> 'a9
-> 'a10
-> 'a11
-> 'b)
-> ('b, 'w) t
val map12
: ('a1, 'w) t
-> ('a2, 'w) t
-> ('a3, 'w) t
-> ('a4, 'w) t
-> ('a5, 'w) t
-> ('a6, 'w) t
-> ('a7, 'w) t
-> ('a8, 'w) t
-> ('a9, 'w) t
-> ('a10, 'w) t
-> ('a11, 'w) t
-> ('a12, 'w) t
-> f:
('a1
-> 'a2
-> 'a3
-> 'a4
-> 'a5
-> 'a6
-> 'a7
-> 'a8
-> 'a9
-> 'a10
-> 'a11
-> 'a12
-> 'b)
-> ('b, 'w) t
val map13
: ('a1, 'w) t
-> ('a2, 'w) t
-> ('a3, 'w) t
-> ('a4, 'w) t
-> ('a5, 'w) t
-> ('a6, 'w) t
-> ('a7, 'w) t
-> ('a8, 'w) t
-> ('a9, 'w) t
-> ('a10, 'w) t
-> ('a11, 'w) t
-> ('a12, 'w) t
-> ('a13, 'w) t
-> f:
('a1
-> 'a2
-> 'a3
-> 'a4
-> 'a5
-> 'a6
-> 'a7
-> 'a8
-> 'a9
-> 'a10
-> 'a11
-> 'a12
-> 'a13
-> 'b)
-> ('b, 'w) t
val map14
: ('a1, 'w) t
-> ('a2, 'w) t
-> ('a3, 'w) t
-> ('a4, 'w) t
-> ('a5, 'w) t
-> ('a6, 'w) t
-> ('a7, 'w) t
-> ('a8, 'w) t
-> ('a9, 'w) t
-> ('a10, 'w) t
-> ('a11, 'w) t
-> ('a12, 'w) t
-> ('a13, 'w) t
-> ('a14, 'w) t
-> f:
('a1
-> 'a2
-> 'a3
-> 'a4
-> 'a5
-> 'a6
-> 'a7
-> 'a8
-> 'a9
-> 'a10
-> 'a11
-> 'a12
-> 'a13
-> 'a14
-> 'b)
-> ('b, 'w) t
val map15
: ('a1, 'w) t
-> ('a2, 'w) t
-> ('a3, 'w) t
-> ('a4, 'w) t
-> ('a5, 'w) t
-> ('a6, 'w) t
-> ('a7, 'w) t
-> ('a8, 'w) t
-> ('a9, 'w) t
-> ('a10, 'w) t
-> ('a11, 'w) t
-> ('a12, 'w) t
-> ('a13, 'w) t
-> ('a14, 'w) t
-> ('a15, 'w) t
-> f:
('a1
-> 'a2
-> 'a3
-> 'a4
-> 'a5
-> 'a6
-> 'a7
-> 'a8
-> 'a9
-> 'a10
-> 'a11
-> 'a12
-> 'a13
-> 'a14
-> 'a15
-> 'b)
-> ('b, 'w) t
end
module type Map_n = sig
type 'a t
include Map_n_gen with type ('a, 'w) t := 'a t
end
module type Bind_n_gen = sig
type ('a, 'w) t
val bind2 : ('a1, 'w) t -> ('a2, 'w) t -> f:('a1 -> 'a2 -> ('b, 'w) t) -> ('b, 'w) t
val bind3
: ('a1, 'w) t
-> ('a2, 'w) t
-> ('a3, 'w) t
-> f:('a1 -> 'a2 -> 'a3 -> ('b, 'w) t)
-> ('b, 'w) t
val bind4
: ('a1, 'w) t
-> ('a2, 'w) t
-> ('a3, 'w) t
-> ('a4, 'w) t
-> f:('a1 -> 'a2 -> 'a3 -> 'a4 -> ('b, 'w) t)
-> ('b, 'w) t
end
module type Bind_n = sig
type 'a t
include Bind_n_gen with type ('a, 'w) t := 'a t
end
(** [S_gen] is the type of the module returned by [Incremental.Make]. It is a
specialization of the interface of [Incremental], with:
- the ['w] state_witness type parameter removed
- the [State.t] argument removed
The comments for components of [S_gen] are in [module type Incremental] below. *)
module type S_gen = sig
module State : sig
type t [@@deriving sexp_of]
include Invariant.S with type t := t
(** [t] is the shared state for a call to [Incremental.Make]. *)
val t : t
val keep_node_creation_backtrace : t -> bool
val set_keep_node_creation_backtrace : t -> bool -> unit
val max_height_allowed : t -> int
val set_max_height_allowed : t -> int -> unit
val num_active_observers : t -> int
val max_height_seen : t -> int
val num_nodes_became_necessary : t -> int
val num_nodes_became_unnecessary : t -> int
val num_nodes_changed : t -> int
val num_nodes_created : t -> int
val num_nodes_invalidated : t -> int
val num_nodes_recomputed : t -> int
val num_nodes_recomputed_directly_because_one_child : t -> int
val num_nodes_recomputed_directly_because_min_height : t -> int
val num_stabilizes : t -> int
val num_var_sets : t -> int
module Stats : sig
type t [@@deriving sexp_of]
end
val stats : t -> Stats.t
end
type 'a t [@@deriving sexp_of]
type 'a incremental := 'a t
include Invariant.S1 with type 'a t := 'a t
val is_const : _ t -> bool
val is_valid : _ t -> bool
val is_necessary : _ t -> bool
val const : 'a -> 'a t
val return : 'a -> 'a t
val map : 'a t -> f:('a -> 'b) -> 'b t
val ( >>| ) : 'a t -> ('a -> 'b) -> 'b t
include Map_n with type 'a t := 'a t
val bind : 'a t -> f:('a -> 'b t) -> 'b t
val ( >>= ) : 'a t -> ('a -> 'b t) -> 'b t
include Bind_n with type 'a t := 'a t
module Infix : sig
val ( >>| ) : 'a t -> ('a -> 'b) -> 'b t
val ( >>= ) : 'a t -> ('a -> 'b t) -> 'b t
end
val join : 'a t t -> 'a t
val if_ : bool t -> then_:'a t -> else_:'a t -> 'a t
val freeze : ?when_:('a -> bool) -> 'a t -> 'a t
val depend_on : 'a t -> depend_on:_ t -> 'a t
val necessary_if_alive : 'a t -> 'a t
val for_all : bool t array -> bool t
val exists : bool t array -> bool t
val all : 'a t list -> 'a list t
val both : 'a t -> 'b t -> ('a * 'b) t
val array_fold : 'a t array -> init:'b -> f:('b -> 'a -> 'b) -> 'b t
val reduce_balanced
: 'a t array
-> f:('a -> 'b)
-> reduce:('b -> 'b -> 'b)
-> 'b t option
module Unordered_array_fold_update : sig
type ('a, 'b) t =
| F_inverse of ('b -> 'a -> 'b)
| Update of ('b -> old_value:'a -> new_value:'a -> 'b)
end
val unordered_array_fold
: ?full_compute_every_n_changes:int
-> 'a t array
-> init:'b
-> f:('b -> 'a -> 'b)
-> update:('a, 'b) Unordered_array_fold_update.t
-> 'b t
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
val opt_sum
: ?full_compute_every_n_changes:int
-> 'a option t array
-> zero:'a
-> add:('a -> 'a -> 'a)
-> sub:('a -> 'a -> 'a)
-> 'a option t
val sum_int : int t array -> int t
val sum_float : float t array -> float t
module Scope : sig
type t
val top : t
val current : unit -> t
val within : t -> f:(unit -> 'a) -> 'a
val is_top : t -> bool
end
module Var : sig
type 'a t [@@deriving sexp_of]
val create : ?use_current_scope:bool -> 'a -> 'a t
val set : 'a t -> 'a -> unit
val watch : 'a t -> 'a incremental
val value : 'a t -> 'a
val latest_value : 'a t -> 'a
val replace : 'a t -> f:('a -> 'a) -> unit
end
module Observer : sig
type 'a t [@@deriving sexp_of]
include Invariant.S1 with type 'a t := 'a t
val observing : 'a t -> 'a incremental
val use_is_allowed : _ t -> bool
val value : 'a t -> 'a Or_error.t
val value_exn : 'a t -> 'a
module Update : sig
type 'a t =
| Initialized of 'a
| Changed of 'a * 'a
| Invalidated
[@@deriving compare, sexp_of]
end
val on_update_exn : 'a t -> f:('a Update.t -> unit) -> unit
val disallow_future_use : _ t -> unit
end
val observe : ?should_finalize:bool -> 'a t -> 'a Observer.t
module Update : sig
type 'a t =
| Necessary of 'a
| Changed of 'a * 'a
| Invalidated
| Unnecessary
[@@deriving compare, sexp_of]
end
val on_update : 'a t -> f:('a Update.t -> unit) -> unit
val stabilize : unit -> unit
val am_stabilizing : unit -> bool
module Cutoff : sig
type 'a t [@@deriving sexp_of]
include Invariant.S1 with type 'a t := 'a t
val create : (old_value:'a -> new_value:'a -> bool) -> 'a t
val of_compare : ('a -> 'a -> int) -> 'a t
val of_equal : ('a -> 'a -> bool) -> 'a t
val always : _ t
val never : _ t
val phys_equal : _ t
val poly_equal : _ t
val should_cutoff : 'a t -> old_value:'a -> new_value:'a -> bool
val equal : 'a t -> 'a t -> bool
end
val set_cutoff : 'a t -> 'a Cutoff.t -> unit
val get_cutoff : 'a t -> 'a Cutoff.t
val lazy_from_fun : (unit -> 'a) -> 'a Lazy.t
val default_hash_table_initial_size : int
val memoize_fun
: ?initial_size:int
-> 'a Base.Hashtbl.Key.t
-> ('a -> 'b)
-> ('a -> 'b) Staged.t
val memoize_fun_by_key
: ?initial_size:int
-> 'key Base.Hashtbl.Key.t
-> ('a -> 'key)
-> ('a -> 'b)
-> ('a -> 'b) Staged.t
val weak_memoize_fun
: ?initial_size:int
-> 'a Base.Hashtbl.Key.t
-> ('a -> 'b Heap_block.t)
-> ('a -> 'b Heap_block.t) Staged.t
val weak_memoize_fun_by_key
: ?initial_size:int
-> 'key Base.Hashtbl.Key.t
-> ('a -> 'key)
-> ('a -> 'b Heap_block.t)
-> ('a -> 'b Heap_block.t) Staged.t
val user_info : _ t -> Info.t option
val set_user_info : _ t -> Info.t option -> unit
val append_user_info_graphviz
: _ t
-> label:string list
-> attrs:string String.Map.t
-> unit
module Node_value : sig
type 'a t =
| Invalid
| Necessary_maybe_stale of 'a option
| Unnecessary_maybe_stale of 'a option
[@@deriving sexp_of]
end
(** [node_value t] returns whatever value [t] happens to have in it, regardless of
whether [t] is valid, necessary, or stale. One should use [observe] for a more
sensible semantics, reserving [node_value] for debugging. *)
val node_value : 'a t -> 'a Node_value.t
module Packed : sig
type t
val save_dot : ?emit_bind_edges:bool -> Out_channel.t -> t list -> unit
val save_dot_to_file : ?emit_bind_edges:bool -> string -> t list -> unit
val append_user_info_graphviz
: t
-> label:string list
-> attrs:string String.Map.t
-> unit
end
val pack : _ t -> Packed.t
val save_dot : ?emit_bind_edges:bool -> Out_channel.t -> unit
val save_dot_to_file : ?emit_bind_edges:bool -> string -> unit
module Let_syntax : sig
val return : 'a -> 'a t
val ( >>| ) : 'a t -> ('a -> 'b) -> 'b t
val ( >>= ) : 'a t -> ('a -> 'b t) -> 'b t
module Let_syntax : sig
val bind : 'a t -> f:('a -> 'b t) -> 'b t
val return : 'a -> 'a t
include Bind_n with type 'a t := 'a t
val map : 'a t -> f:('a -> 'b) -> 'b t
include Map_n with type 'a t := 'a t
val both : 'a t -> 'b t -> ('a * 'b) t
module Open_on_rhs : sig
val watch : 'a Var.t -> 'a t
end
end
end
module Before_or_after : sig
type t =
| Before
| After
[@@deriving sexp_of]
end
module Step_function = Step_function
module Clock : sig
type t [@@deriving sexp_of]
val default_timing_wheel_config : Timing_wheel.Config.t
val create
: ?timing_wheel_config:Timing_wheel.Config.t
-> start:Time_ns.t
-> unit
-> t
val alarm_precision : t -> Time_ns.Span.t
val timing_wheel_length : t -> int
val now : t -> Time_ns.t
val watch_now : t -> Time_ns.t incremental
val advance_clock : t -> to_:Time_ns.t -> unit
val advance_clock_by : t -> Time_ns.Span.t -> unit
val at : t -> Time_ns.t -> Before_or_after.t incremental
val after : t -> Time_ns.Span.t -> Before_or_after.t incremental
val at_intervals : t -> Time_ns.Span.t -> unit incremental
val step_function : t -> init:'a -> (Time_ns.t * 'a) list -> 'a incremental
val incremental_step_function : t -> 'a Step_function.t incremental -> 'a incremental
val snapshot
: t
-> 'a incremental
-> at:Time_ns.t
-> before:'a
-> 'a incremental Or_error.t