A lightweight reactive document library.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

152 lines
4.9 KiB

  1. (* Sequence construction
  2. [Lwd_seq] implements a type of ordered collections with a pure interface.
  3. In addition, changes to collections are easy to track.
  4. A collection can be transformed with the usual map, filter and fold
  5. combinators. If later, the transformation is applied again to an updated
  6. collection, shared elements (in the sense of physical sharing), the
  7. result of the previous transformation will be reused for these elements.
  8. The book-keeping overhead is O(n) in the number of changes, so O(1) per
  9. element.
  10. *)
  11. type +'a t
  12. type +'a seq = 'a t
  13. (* A sequence with no element. *)
  14. val empty : 'a seq
  15. (* A singleton sequence. The physical identity of the element is considered
  16. when reusing previous computations.
  17. If you do:
  18. let x1 = element x
  19. let x2 = element x
  20. Then x1 and x2 are seen as different elements and no sharing will be done
  21. during transformation.
  22. *)
  23. val element : 'a -> 'a seq
  24. (* Concatenate two sequences into a bigger one.
  25. As for [element], the physical identity of a sequence is considered for
  26. reuse.
  27. *)
  28. val concat : 'a seq -> 'a seq -> 'a seq
  29. (* Look at the contents of a sequence *)
  30. type ('a, 'b) view =
  31. | Empty
  32. | Element of 'a
  33. | Concat of 'b * 'b
  34. val view : 'a seq -> ('a, 'a seq) view
  35. module Balanced : sig
  36. (* A variant of the sequence type that guarantees that the depth of
  37. transformation, as measured in the number of [concat] nodes, grows in
  38. O(log n) where n is the number of elements in the sequnce.
  39. This is useful to prevent stack overflows and to avoid degenerate cases
  40. where a single element change, but it is at the end of a linear sequence
  41. of [concat] nodes, thus making the total work O(n).
  42. For instance, in:
  43. [concat e1 (concat e2 (concat e3 (... (concat e_n))...))]
  44. If [e_n] changes, the whole spine has to be recomputed.
  45. Using [Balanced.concat], the representation will be re-balanced
  46. internally. Then [Balanced.view] should be used to access the balanced
  47. sequence.
  48. When working with balanced sequences in a transformation pipeline, it is
  49. only useful to balance the first sequence of the pipeline. Derived
  50. sequence will have a depth bounded by the depth of the first one.
  51. *)
  52. type 'a t = private 'a seq
  53. val empty : 'a t
  54. val element : 'a -> 'a t
  55. val concat : 'a t -> 'a t -> 'a t
  56. val view : 'a t -> ('a, 'a t) view
  57. end
  58. (* Lwd interface.
  59. All sequences live in [Lwd] monad: if a sequence changes slightly, parts
  60. that have not changed will not be re-transformed.
  61. *)
  62. (* [fold ~map ~reduce] transforms a sequence.
  63. If the sequence is non-empty, the [map] function is applied to element nodes
  64. and the [reduce] function is used to combine transformed concatenated nodes.
  65. If the sequence is empty, None is returned.
  66. *)
  67. val fold :
  68. map:('a -> 'b) -> reduce:('b -> 'b -> 'b) -> 'a seq Lwd.t -> 'b option Lwd.t
  69. val fold_monoid :
  70. ('a -> 'b) -> 'b Lwd_utils.monoid -> 'a seq Lwd.t -> 'b Lwd.t
  71. (* [map f] transforms a sequence by applying [f] to each element. *)
  72. val map :
  73. ('a -> 'b) -> 'a seq Lwd.t -> 'b seq Lwd.t
  74. val filter :
  75. ('a -> bool) -> 'a seq Lwd.t -> 'a seq Lwd.t
  76. val filter_map :
  77. ('a -> 'b option) -> 'a seq Lwd.t -> 'b seq Lwd.t
  78. val lift : 'a Lwd.t seq Lwd.t -> 'a seq Lwd.t
  79. (* Low-level interface *)
  80. module Reducer : sig
  81. (* The interface allows to implement incremental sequence transformation
  82. outside of the [Lwd] monad.
  83. Actually, the Lwd functions above are implemented on top of this
  84. interface.
  85. *)
  86. (* A [('a, 'b) reducer] value stores the state necessary to incrementally
  87. transform an ['a seq] to ['b].
  88. In essence, the Lwd functions just hide a reducer value.
  89. *)
  90. type ('a, 'b) reducer
  91. (* A new reducer that transforms sequences with the given [map] and [reduce]
  92. functions. The reducer starts from the [empty] sequence. *)
  93. val make : map:('a -> 'b) -> reduce:('b -> 'b -> 'b) -> ('a, 'b) reducer
  94. (* Updates the [reducer] to transform another sequence.
  95. Intermediate nodes are reused when possible.
  96. Only the "reuse plan" is computed by [update], actual transformation is
  97. done by the [reduce] function.
  98. *)
  99. val update : ('a, 'b) reducer -> 'a seq -> ('a, 'b) reducer
  100. (* Returns the reduced ['b] value if the sequence is non-empty or [None] if
  101. the sequence is empty.
  102. Because transformation is done lazily, [reduce] is the only function
  103. that can call [map] and [reduce].
  104. *)
  105. val reduce : ('a, 'b) reducer -> 'b option
  106. (* Sometimes it is important to track the elements that disappeared from a
  107. sequence. The ['b dropped] type represent all the intermediate result that
  108. were referenced by a reducer and are no longer after an update.
  109. *)
  110. type 'b dropped
  111. val update_and_get_dropped :
  112. ('a, 'b) reducer -> 'a seq -> 'b dropped * ('a, 'b) reducer
  113. val fold_dropped :
  114. [<`All|`Map|`Reduce] -> ('a -> 'b -> 'b) -> 'a dropped -> 'b -> 'b
  115. end