Module Cumulus
Differential Signals
type ('a, 'da) ttype ('a, 'da) change=|Init of 'aan initial value of a cumulus signal
|Patch of 'a * 'dathe latest value and change of a cumulus signal
|Keep of 'athe latest value of an unmodified signal
('a, 'da) changerepresents the value and latest change of a cumulus signal. Normally a signal goes fromInitto a series ofPatchpossibly. The series ofPatches may be interspersed withKeepfor functions which observe the signal between its changes.Initmay also occur later in the series for some cumulus signals and indicates that it has been reinitialized. Such a cumulus signals are said to be discontinuous.
type ('a, 'da) update= 'a -> ('a, 'da) changeA shortcut for the function type used to represent updates to a cumulus signal. The function takes the current value of the signal and the result indicates the next value and how it changed from the current value.
Patch (x', dx)announces that the value changed tox'withdxholding information about the difference from the current valuextox'.Keep xannounces that the cumulus signal will remain unchanged, wherexmust be the argument to the update function.Init x'can be used to reinitialize the cumulus signal, introducing a discontinuity.
Construction and Integration
val const : 'a -> ('a, 'da) tconst vis the cumulus signal holding the constant valuev.
val create : 'a -> ('a, 'da) t * (?step:React.Step.t -> ('a, 'da) change -> unit)create vis a(v, f)wherevis a new cumulus signal starting with the valuevand which can be updated by callingf.
val of_event : 'a React.event -> (unit, 'a) tof_event eis the cumulus signal having no state and reporting events fromeas its changes.
val of_signal : 'a React.signal -> ('a, unit) tof_signal sis a cumulus signal holding the same value assat any time while providing no differential information about the changes.
val hold : 'a -> 'a React.event -> ('a, unit) thold v eis the cumulus signal starting atv, then taking values fromewhile providing no differential information about the changes.
val integrate : ('da -> 'a -> 'a) -> 'da React.event -> 'a -> ('a, 'da) tintegrate f e vis the cumulus signal starting off withvthen for each occurrencedxofe, then signal is updated byf dxwithdxreported as the change.
Stopping
val stop : ?strong:bool -> ('a, 'da) t -> unitstop cchangescinto a constant cumulus signal holding the latest value. Thestrongparameter is relevant for platforms which don't have weak references. See the React library for details.
Observation
val value : ('a, 'da) t -> 'avalue cis the currently held value ofc. This should not be called during an update step.
val signal : ('a, 'da) t -> 'a React.signalsignal cis the react signal holding the same value ascat any time.
Full Lifting
val lift1 : init:('a -> 'b) -> patch:(('a, 'da) change -> ('b, 'db) update) -> ('a, 'da) t -> ('b, 'db) tval lift2 : init:('a -> 'b -> 'c) -> patch:(('a, 'da) change -> ('b, 'db) change -> ('c, 'dc) update) -> ('a, 'da) t -> ('b, 'db) t -> ('c, 'dc) tval lift3 : init:('a -> 'b -> 'c -> 'd) -> patch:(('a, 'da) change -> ('b, 'db) change -> ('c, 'dc) change -> ('d, 'dd) update) -> ('a, 'da) t -> ('b, 'db) t -> ('c, 'dc) t -> ('d, 'dd) t
Simplified Lifting
These functions cover the common case of deriving new cumulus signals from existing ones. This is done by providing a function ~init which constructs the initial value from the latest values of the source signals, and a function ~patch which provides an update given the latest values and changes of the source signals. The following example combines two cumulus signals by pairing the values and combining the differentials:
let c =
let init x1 x2 = (x1, x2) in
let patch (x1, dx1) (x2, dx2) y =
(match dx1, dx2 with
| None, None -> Cumulus.Keep y
| Some dx1, None -> Cumulus.Patch ((x1, x2), `Left dx1)
| None, Some dx2 -> Cumulus.Patch ((x1, x2), `Right dx2)
| Some dx1, Some dx2 -> Cumulus.Patch ((x1, x2), `Both (dx1, dx2)))
in
Cumulus.l2 ~init ~patch c1 c2The main difference from full lifting is that a discontinuity of any of the source signals will cause a discontinuity in the constructed signal, meaning that ~init will be called instead of ~patch, which for the common case of continuous cumulus signals makes no difference.
val l1 : init:('a -> 'b) -> patch:(('a * 'da) -> ('b, 'db) update) -> ('a, 'da) t -> ('b, 'db) tval l2 : init:('a -> 'b -> 'c) -> patch:(('a * 'da option) -> ('b * 'db option) -> ('c, 'dc) update) -> ('a, 'da) t -> ('b, 'db) t -> ('c, 'dc) tval l3 : init:('a -> 'b -> 'c -> 'd) -> patch:(('a * 'da option) -> ('b * 'db option) -> ('c * 'dc option) -> ('d, 'dd) update) -> ('a, 'da) t -> ('b, 'db) t -> ('c, 'dc) t -> ('d, 'dd) tval l4 : init:('a -> 'b -> 'c -> 'd -> 'e) -> patch:(('a * 'da option) -> ('b * 'db option) -> ('c * 'dc option) -> ('d * 'dd option) -> ('e, 'de) update) -> ('a, 'da) t -> ('b, 'db) t -> ('c, 'dc) t -> ('d, 'dd) t -> ('e, 'de) tval l5 : init:('a -> 'b -> 'c -> 'd -> 'e -> 'f) -> patch:(('a * 'da option) -> ('b * 'db option) -> ('c * 'dc option) -> ('d * 'dd option) -> ('e * 'de option) -> ('f, 'df) update) -> ('a, 'da) t -> ('b, 'db) t -> ('c, 'dc) t -> ('d, 'dd) t -> ('e, 'de) t -> ('f, 'df) tval l6 : init:('a -> 'b -> 'c -> 'd -> 'e -> 'f -> 'g) -> patch:(('a * 'da option) -> ('b * 'db option) -> ('c * 'dc option) -> ('d * 'dd option) -> ('e * 'de option) -> ('f * 'df option) -> ('g, 'dg) update) -> ('a, 'da) t -> ('b, 'db) t -> ('c, 'dc) t -> ('d, 'dd) t -> ('e, 'de) t -> ('f, 'df) t -> ('g, 'dg) tval lN : init:('a list -> 'b) -> patch:(('a * 'da option) list -> ('b, 'db) update) -> ('a, 'da) t list -> ('b, 'db) t